testing account configuration

This commit is contained in:
Roland Osborne 2022-03-08 13:31:04 -08:00
parent 03fbf2934b
commit dde82cd37b
19 changed files with 214 additions and 47 deletions

View File

@ -2979,10 +2979,10 @@ components:
disabled:
type: boolean
storageUsed:
type: number
type: integer
format: int64
storageAvailable:
type: number
type: integer
format: int64
forwardingAddress:
type: string

View File

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

View File

@ -12,7 +12,7 @@ import (
func AddAccount(w http.ResponseWriter, r *http.Request) {
token, res := BearerAccountToken(r);
if res != nil || token.TokenType != APP_ACCOUNTCREATE {
if res != nil || token.TokenType != APP_TOKENCREATE {
ErrResponse(w, http.StatusUnauthorized, res)
return
}

View File

@ -10,7 +10,7 @@ import (
func AddAccountApp(w http.ResponseWriter, r *http.Request) {
id, err := AccountLogin(r)
account, err := AccountLogin(r)
if err != nil {
ErrResponse(w, http.StatusUnauthorized, err)
return
@ -24,8 +24,8 @@ func AddAccountApp(w http.ResponseWriter, r *http.Request) {
token := hex.EncodeToString(data)
accountToken := store.AccountToken{
AccountID: id,
TokenType: APP_ACCOUNTATTACH,
AccountID: account.ID,
TokenType: APP_TOKENATTACH,
Token: token,
Expires: time.Now().Unix() + APP_ATTACHEXPIRE,
}

View File

@ -10,13 +10,13 @@ import (
func AddAccountAuthentication(w http.ResponseWriter, r *http.Request) {
id, err := AccountLogin(r)
account, err := AccountLogin(r)
if err != nil {
ErrResponse(w, http.StatusUnauthorized, err)
return
}
data, res := securerandom.Bytes(4)
data, res := securerandom.Bytes(APP_RESETSIZE)
if res != nil {
ErrResponse(w, http.StatusInternalServerError, res)
return
@ -24,8 +24,8 @@ func AddAccountAuthentication(w http.ResponseWriter, r *http.Request) {
token := hex.EncodeToString(data)
accountToken := store.AccountToken{
AccountID: id,
TokenType: APP_ACCOUNTRESET,
AccountID: account.ID,
TokenType: APP_TOKENRESET,
Token: token,
Expires: time.Now().Unix() + APP_RESETEXPIRE,
}

View File

@ -15,7 +15,7 @@ func AddNodeAccount(w http.ResponseWriter, r *http.Request) {
return
}
data, err := securerandom.Bytes(16)
data, err := securerandom.Bytes(APP_CREATESIZE)
if err != nil {
ErrResponse(w, http.StatusInternalServerError, err)
return
@ -23,7 +23,7 @@ func AddNodeAccount(w http.ResponseWriter, r *http.Request) {
token := hex.EncodeToString(data)
accountToken := store.AccountToken{
TokenType: "create",
TokenType: APP_TOKENCREATE,
Token: token,
Expires: time.Now().Unix() + APP_CREATEEXPIRE,
};

View File

@ -0,0 +1,36 @@
package databag
import (
"net/http"
"databag/internal/store"
)
func GetAccountStatus(w http.ResponseWriter, r *http.Request) {
account, err := AccountLogin(r)
if err != nil {
ErrResponse(w, http.StatusUnauthorized, err)
return
}
var assets []store.Asset;
if err = store.DB.Where("account_id = ?", account.ID).Find(&assets).Error; err != nil {
ErrResponse(w, http.StatusInternalServerError, err)
return
}
// construct response
status := &AccountStatus{}
status.StorageAvailable = getNumConfigValue(CONFIG_STORAGE, 0);
for _, asset := range assets {
status.StorageUsed += asset.Size
}
status.Disabled = account.Disabled
status.ForwardingAddress = account.Forward
status.Searchable = account.Searchable
WriteResponse(w, status)
}

View File

@ -22,7 +22,7 @@ func GetChannelTopic(w http.ResponseWriter, r *http.Request) {
// load topic
var topicSlot store.TopicSlot
if err = store.DB.Preload("Topic").Where("channel_id = ? AND topic_slot_id = ?", channelSlot.Channel.ID, topicId).First(&topicSlot).Error; err != nil {
if err = store.DB.Preload("Topic.Assets").Where("channel_id = ? AND topic_slot_id = ?", channelSlot.Channel.ID, topicId).First(&topicSlot).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
code = http.StatusNotFound
} else {

View File

@ -37,7 +37,7 @@ func GetChannelTopics(w http.ResponseWriter, r *http.Request) {
}
} else {
var slots []store.TopicSlot
if err := store.DB.Preload("Topic").Where("channel_id = ?", channelSlot.Channel.ID).Find(&slots).Error; err != nil {
if err := store.DB.Preload("Topic.Assets").Where("channel_id = ?", channelSlot.Channel.ID).Find(&slots).Error; err != nil {
ErrResponse(w, http.StatusInternalServerError, err)
return
}

View File

@ -11,7 +11,7 @@ import (
func SetAccountApp(w http.ResponseWriter, r *http.Request) {
token, res := BearerAccountToken(r);
if res != nil || token.TokenType != APP_ACCOUNTATTACH {
if res != nil || token.TokenType != APP_TOKENATTACH {
ErrResponse(w, http.StatusUnauthorized, res)
return
}

View File

@ -3,13 +3,14 @@ package databag
import (
"errors"
"net/http"
"gorm.io/gorm"
"databag/internal/store"
)
func SetAccountAuthentication(w http.ResponseWriter, r *http.Request) {
token, res := BearerAccountToken(r)
if res != nil || token.TokenType != APP_ACCOUNTRESET {
if res != nil || token.TokenType != APP_TOKENRESET {
ErrResponse(w, http.StatusUnauthorized, res)
return
}
@ -19,14 +20,23 @@ func SetAccountAuthentication(w http.ResponseWriter, r *http.Request) {
}
username, password, ret := BasicCredentials(r)
if ret != nil {
ErrResponse(w, http.StatusUnauthorized, ret)
if ret != nil || username == "" || password == nil || len(password) == 0 {
ErrResponse(w, http.StatusBadRequest, errors.New("invalid credentials"))
return
}
token.Account.Username = username;
token.Account.Password = password;
if err := store.DB.Save(token.Account).Error; err != nil {
err := store.DB.Transaction(func(tx *gorm.DB) error {
if res := tx.Save(token.Account).Error; res != nil {
return res
}
if res := tx.Delete(token).Error; res != nil {
return res
}
return nil
})
if err != nil {
ErrResponse(w, http.StatusInternalServerError, err)
return
}

View File

@ -4,8 +4,11 @@ const APP_TOKENSIZE = 16
const APP_BODYLIMIT = 1048576
const APP_VERSION = "0.0.1"
const APP_ATTACHEXPIRE = 300
const APP_ATTACHSIZE = 4
const APP_CREATEEXPIRE = 86400
const APP_CREATESIZE = 16
const APP_RESETEXPIRE = 86400
const APP_RESETSIZE = 16
const APP_CONNECTEXPIRE = 30
const APP_KEYSIZE = 4096
const APP_RSA4096 = "RSA4096"
@ -27,6 +30,9 @@ const APP_NOTIFYCHANNEL = "channel"
const APP_NOTIFYVIEW = "view"
const APP_TOKENAPP = "app"
const APP_TOKENCONTACT = "contact"
const APP_TOKENATTACH = "attach"
const APP_TOKENCREATE = "create"
const APP_TOKENRESET = "reset"
const APP_NOTIFYBUFFER = 4096
const APP_TOPICUNCONFIRMED = "unconfirmed"
const APP_TOPICCONFIRMED = "confirmed"
@ -41,9 +47,6 @@ const APP_QUEUEAUDIO = "audio"
const APP_QUEUEVIDEO = "video"
const APP_QUEUEPHOTO = "photo"
const APP_QUEUEDEFAULT = ""
const APP_ACCOUNTATTACH = "attach"
const APP_ACCOUNTCREATE = "create"
const APP_ACCOUNTRESET = "reset"
func AppCardStatus(status string) bool {
if status == APP_CARDPENDING {

View File

@ -11,12 +11,6 @@ import (
"databag/internal/store"
)
type accountLogin struct {
ID uint
Guid string
Password []byte
}
func AdminLogin(r *http.Request) error {
// extract request auth
@ -44,26 +38,26 @@ func AdminLogin(r *http.Request) error {
return nil
}
func AccountLogin(r *http.Request) (uint, error) {
func AccountLogin(r *http.Request) (*store.Account, error) {
// extract request auth
username, password, ok := r.BasicAuth();
if !ok || username == "" || password == "" {
return 0, errors.New("invalid login")
return nil, errors.New("invalid login")
}
// find account
var account accountLogin
account := &store.Account{}
if store.DB.Model(&store.Account{}).Where("Username = ?", username).First(&account).Error != nil {
return 0, errors.New("username not found");
return nil, errors.New("username not found");
}
// compare password
if bcrypt.CompareHashAndPassword(account.Password, []byte(password)) != nil {
return 0, errors.New("invalid password");
return nil, errors.New("invalid password");
}
return account.ID, nil
return account, nil
}
func BearerAccountToken(r *http.Request) (store.AccountToken, error) {

View File

@ -17,9 +17,9 @@ type AccountStatus struct {
Disabled bool `json:"disabled"`
StorageUsed float64 `json:"storageUsed"`
StorageUsed int64 `json:"storageUsed"`
StorageAvailable float64 `json:"storageAvailable"`
StorageAvailable int64 `json:"storageAvailable"`
ForwardingAddress string `json:"forwardingAddress"`

View File

@ -68,6 +68,8 @@ type Account struct {
Created int64 `gorm:"autoCreateTime"`
Updated int64 `gorm:"autoUpdateTime"`
Disabled bool `gorm:"not null;default:false"`
Searchable bool `gorm:"not null;default:false"`
Forward string
AccountDetail AccountDetail
Apps []App
}

View File

@ -831,6 +831,8 @@ type TestApiParams struct {
body interface{}
tokenType string
token string
authorization string
credentials string
}
type TestApiResponse struct {
@ -857,6 +859,12 @@ func TestApiRequest(endpoint func(http.ResponseWriter, *http.Request), params *T
if params.token != "" {
SetBearerAuth(r, params.token)
}
if params.authorization != "" {
SetBasicAuth(r, params.authorization)
}
if params.credentials != "" {
SetCredentials(r, params.credentials)
}
endpoint(w, r)
res := w.Result()
@ -864,6 +872,7 @@ func TestApiRequest(endpoint func(http.ResponseWriter, *http.Request), params *T
err = errors.New("response failed");
return
}
if resp != nil {
resp.header = res.Header
if resp.data != nil {
dec := json.NewDecoder(res.Body)
@ -871,6 +880,7 @@ func TestApiRequest(endpoint func(http.ResponseWriter, *http.Request), params *T
return
}
}
}
return
}

View File

@ -598,7 +598,7 @@ func AddTestAccount(username string) (guid string, token string, err error) {
}
// set account profile
if r, w, err = NewRequest("GET", "/account/profile", nil); err != nil {
if r, w, err = NewRequest("POST", "/account/profile", nil); err != nil {
return
}
SetBearerAuth(r, access);

View File

@ -1,6 +1,7 @@
package databag
import (
"time"
"os"
"io"
"hash/crc32"
@ -105,6 +106,8 @@ func transcodeAsset(asset *store.Asset) {
cmd.Stdout = &stdout
var stderr bytes.Buffer
cmd.Stderr = &stderr
time.Sleep(time.Second);
if err := cmd.Run(); err != nil {
LogMsg(stdout.String())
LogMsg(stderr.String())
@ -149,6 +152,9 @@ func UpdateAsset(asset *store.Asset, status string, crc uint32, size int64) (err
if res := tx.Model(&asset.Topic.TopicSlot).Update("revision", act.ChannelRevision + 1).Error; res != nil {
return res
}
if res := tx.Model(&asset.Channel).Update("topic_revision", act.ChannelRevision + 1).Error; res != nil {
return res
}
if res := tx.Model(&asset.Channel.ChannelSlot).Update("revision", act.ChannelRevision + 1).Error; res != nil {
return res
}

View File

@ -0,0 +1,111 @@
package databag
import (
"testing"
"github.com/stretchr/testify/assert"
"encoding/json"
"encoding/base64"
"net/url"
)
func TestAccountConfig(t *testing.T) {
var params *TestApiParams
var response *TestApiResponse
var channel *Channel
var topic *Topic
var assets *[]Asset
var subject *Subject
var pathParams *map[string]string
// setup testing group
set, err := AddTestGroup("accountconfig")
assert.NoError(t, err)
// allocate testing app
app := NewTestApp()
go app.Connect(set.A.Token)
// asset to post
image := "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"
img, _ := base64.StdEncoding.DecodeString(image)
// get reset token
var token string
params = &TestApiParams{ query: "/account/auth", authorization: "accountconfigA:pass" }
response = &TestApiResponse{ data: &token }
assert.NoError(t, TestApiRequest(AddAccountAuthentication, params, response))
// set reset token
params = &TestApiParams{ query: "/account/auth", tokenType: APP_TOKENRESET, token: token, credentials: "newguy:ssap" }
assert.NoError(t, TestApiRequest(SetAccountAuthentication, params, nil))
// fail getting reset token
params = &TestApiParams{ query: "/account/auth", authorization: "accountconfigA:pass" }
response = &TestApiResponse{ data: &token }
assert.Error(t, TestApiRequest(AddAccountAuthentication, params, response))
// create new channel
channel = &Channel{}
subject = &Subject{ Data: "channeldata", DataType: "channeldatatype" }
params = &TestApiParams{ query: "/content/channels", tokenType: APP_TOKENAPP, token: set.A.Token, body: subject }
response = &TestApiResponse{ data: channel }
assert.NoError(t, TestApiRequest(AddChannel, params, response))
// create new topic
topic = &Topic{}
subject = &Subject{ DataType: "topicdatatype", Data: "topicdata" }
params = &TestApiParams{ query: "/content/channels/{channelId}/topics", tokenType: APP_TOKENAPP, token: set.A.Token,
path: map[string]string{ "channelId": channel.Id }, body: subject }
response = &TestApiResponse{ data: topic }
assert.NoError(t, TestApiRequest(AddChannelTopic, params, response))
// add asset to topic
assets = &[]Asset{}
pathParams = &map[string]string{ "channelId": channel.Id, "topicId": topic.Id }
transforms, err := json.Marshal([]string{ "copy;photo", "copy;photo", })
assert.NoError(t, err)
assert.NoError(t, ApiTestUpload(AddChannelTopicAsset, "POST",
"/content/channels/{channelId}/topics/{topicId}/assets?transforms=" + url.QueryEscape(string(transforms)),
pathParams, img, APP_TOKENAPP, set.A.Token, assets, nil))
// update topic
status := APP_TOPICCONFIRMED
params = &TestApiParams{ query: "/content/channels/{channelId}/topics/{topicId}", tokenType: APP_TOKENAPP, token: set.A.Token,
path: map[string]string{ "channelId": channel.Id, "topicId": topic.Id }, body: &status }
assert.NoError(t, TestApiRequest(SetChannelTopicConfirmed, params, nil))
// wait for assets
assert.NoError(t, app.WaitFor(func(testApp *TestApp)bool {
for _, testChannel := range testApp.channels {
if testChannel.channel.Id == channel.Id {
for _, testTopic := range testChannel.topics {
if testTopic.topic.Id == topic.Id {
detail := testTopic.topic.Data.TopicDetail
if detail.Status == APP_TOPICCONFIRMED && detail.Transform == APP_TRANSFORMCOMPLETE {
return true
}
}
}
}
}
return false
}))
// get account status
accountStatus := &AccountStatus{}
params = &TestApiParams{ query: "/account/status", authorization: "newguy:ssap",
path: map[string]string{ "channelId": channel.Id, "topicId": topic.Id } }
response = &TestApiResponse{ data: accountStatus }
assert.NoError(t, TestApiRequest(GetAccountStatus, params, response))
// add asset to topic
assets = &[]Asset{}
pathParams = &map[string]string{ "channelId": channel.Id, "topicId": topic.Id }
assert.Error(t, ApiTestUpload(AddChannelTopicAsset, "POST",
"/content/channels/{channelId}/topics/{topicId}/assets?transforms=" + url.QueryEscape(string(transforms)),
pathParams, img, APP_TOKENAPP, set.A.Token, assets, nil))
PrintMsg(accountStatus)
}