creating contact from identity message

This commit is contained in:
Roland Osborne 2022-01-20 15:19:26 -08:00
parent 59ae141d31
commit 7eb7cb8f61
16 changed files with 230 additions and 109 deletions

View File

@ -4089,8 +4089,12 @@ components:
CardProfile:
type: object
required:
- guid
- version
- node
properties:
guid:
type: string
handle:
type: string
name:
@ -4104,6 +4108,8 @@ components:
format: int64
imageSet:
type: boolean
version:
type: string
node:
type: string

View File

@ -4,6 +4,7 @@ go 1.17
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/gorilla/mux v1.8.0 // indirect
github.com/gorilla/websocket v1.4.2 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect

View File

@ -2,6 +2,8 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=

View File

@ -0,0 +1,112 @@
package databag
import (
"errors"
"net/http"
"gorm.io/gorm"
"github.com/google/uuid"
"databag/internal/store"
)
func AddCard(w http.ResponseWriter, r *http.Request) {
account, code, err := BearerAppToken(r, false)
if err != nil {
ErrResponse(w, code, err)
return
}
var message DataMessage
if err := ParseRequest(r, w, &message); err != nil {
ErrResponse(w, http.StatusBadRequest, err)
return
}
var identity Identity
guid, messageType, _, err := ReadDataMessage(&message, &identity)
if messageType != APP_MSGIDENTITY || err != nil {
ErrResponse(w, http.StatusBadRequest, err)
return
}
var card store.Card
if err := store.DB.Preload("Groups").Where("account_id = ? AND guid = ?", account.ID, guid).First(&card).Error; err != nil {
if !errors.Is(err, gorm.ErrRecordNotFound) {
ErrResponse(w, http.StatusInternalServerError, err)
return
}
} else {
// update record if revision changed
if identity.Revision > card.ProfileRevision {
card.Username = identity.Handle
card.Name = identity.Name
card.Description = identity.Description
card.Location = identity.Location
card.Image = identity.Image
card.Version = identity.Version
card.Node = identity.Node
card.ProfileRevision = identity.Revision
if err := store.DB.Save(&card).Error; err != nil {
ErrResponse(w, http.StatusInternalServerError, err)
return
}
}
WriteResponse(w, getCardModel(&card))
return
}
// save new record
card.CardId = uuid.New().String()
card.AccountID = account.ID
card.Guid = guid
card.Username = identity.Handle
card.Name = identity.Name
card.Description = identity.Description
card.Location = identity.Location
card.Image = identity.Image
card.Version = identity.Version
card.Node = identity.Node
card.ProfileRevision = identity.Revision
card.Status = APP_CARDCONFIRMED
if err := store.DB.Save(&card).Error; err != nil {
ErrResponse(w, http.StatusInternalServerError, err)
return
}
WriteResponse(w, getCardModel(&card))
}
func getCardModel(card *store.Card) *Card {
// populate group id list
var groups []string;
for _, group := range card.Groups {
groups = append(groups, group.GroupId)
}
return &Card{
CardId: card.CardId,
ProfileRevision: card.RemoteProfile,
ContentRevision: card.RemoteContent,
CardProfile: &CardProfile{
Guid: card.Guid,
Handle: card.Username,
Name: card.Name,
Description: card.Description,
Location: card.Location,
Revision: card.ProfileRevision,
ImageSet: card.Image != "",
Version: card.Version,
Node: card.Node,
},
CardData: &CardData {
Revision: card.DataRevision,
Status: card.Status,
Notes: card.Notes,
Groups: groups,
},
}
}

View File

@ -6,13 +6,9 @@ import (
func Authorize(w http.ResponseWriter, r *http.Request) {
account, res := BearerAppToken(r, true);
account, code, res := BearerAppToken(r, true);
if res != nil {
ErrResponse(w, http.StatusUnauthorized, res)
return
}
if account.Disabled {
ErrResponse(w, http.StatusGone, res)
ErrResponse(w, code, res)
return
}
detail := account.AccountDetail

View File

@ -13,11 +13,6 @@ import (
"net/http"
)
func AddCard(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
w.WriteHeader(http.StatusOK)
}
func ClearCardGroup(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
w.WriteHeader(http.StatusOK)

View File

@ -6,13 +6,9 @@ import (
func GetProfile(w http.ResponseWriter, r *http.Request) {
account, err := BearerAppToken(r, true);
account, code, err := BearerAppToken(r, true);
if err != nil {
ErrResponse(w, http.StatusUnauthorized, err)
return
}
if account.Disabled {
ErrResponse(w, http.StatusGone, nil)
ErrResponse(w, code, err)
return
}
detail := account.AccountDetail

View File

@ -6,13 +6,9 @@ import (
func GetProfileMessage(w http.ResponseWriter, r *http.Request) {
account, res := BearerAppToken(r, true);
account, code, res := BearerAppToken(r, true);
if res != nil {
ErrResponse(w, http.StatusUnauthorized, res)
return
}
if account.Disabled {
ErrResponse(w, http.StatusGone, res)
ErrResponse(w, code, res)
return
}
detail := account.AccountDetail

View File

@ -8,13 +8,9 @@ import (
func SetProfile(w http.ResponseWriter, r *http.Request) {
account, err := BearerAppToken(r, true);
account, code, err := BearerAppToken(r, true);
if err != nil {
ErrResponse(w, http.StatusUnauthorized, err)
return
}
if account.Disabled {
ErrResponse(w, http.StatusGone, nil)
ErrResponse(w, code, err)
return
}
detail := account.AccountDetail
@ -47,7 +43,6 @@ func SetProfile(w http.ResponseWriter, r *http.Request) {
}
SetStatus(account)
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
w.WriteHeader(http.StatusOK)
WriteResponse(w, nil)
}

View File

@ -59,7 +59,7 @@ func Status(w http.ResponseWriter, r *http.Request) {
}
// send current version
rev := getRevision(app.Account)
rev := getRevision(&app.Account)
var msg []byte
msg, err = json.Marshal(rev)
if err != nil {
@ -95,7 +95,7 @@ func Status(w http.ResponseWriter, r *http.Request) {
}
}
func getRevision(account store.Account) Revision {
func getRevision(account *store.Account) Revision {
var r Revision
r.Profile = account.ProfileRevision
r.Content = account.ContentRevision
@ -111,7 +111,7 @@ func ExitStatus() {
wsExit <- true
}
func SetStatus(account store.Account) {
func SetStatus(account *store.Account) {
// get revisions for the account
rev := getRevision(account);

View File

@ -13,3 +13,9 @@ const APP_MSGAUTHENTICATE = "authenticate"
const APP_MSGIDENTITY = "identity"
const APP_MSGCONNECT = "connect"
const APP_MSGDISCONNECT = "disconnect"
const APP_CARDPENDING = "pending"
const APP_CARDCONFIRMED = "confirmed"
const APP_CARDREQUESTED = "requested"
const APP_CARDCONNECTING = "connecting"
const APP_CARDCONNECTED = "connected"

View File

@ -6,6 +6,7 @@ import (
"time"
"net/http"
"encoding/base64"
"gorm.io/gorm"
"golang.org/x/crypto/bcrypt"
"databag/internal/store"
)
@ -81,7 +82,7 @@ func BearerAccountToken(r *http.Request) (store.AccountToken, error) {
return accountToken, nil
}
func BearerAppToken(r *http.Request, detail bool) (store.Account, error) {
func BearerAppToken(r *http.Request, detail bool) (*store.Account, int, error) {
// parse bearer authentication
auth := r.Header.Get("Authorization")
@ -91,14 +92,26 @@ func BearerAppToken(r *http.Request, detail bool) (store.Account, error) {
var app store.App
if detail {
if err := store.DB.Preload("Account.AccountDetail").Where("token = ?", token).First(&app).Error; err != nil {
return app.Account, err
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, http.StatusNotFound, err
} else {
return nil, http.StatusInternalServerError, err
}
}
} else {
if err := store.DB.Preload("Account").Where("token = ?", token).First(&app).Error; err != nil {
return app.Account, err
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, http.StatusNotFound, err
} else {
return nil, http.StatusInternalServerError, err
}
}
return app.Account, nil
}
if app.Account.Disabled {
return nil, http.StatusGone, errors.New("account is inactive")
}
return &app.Account, http.StatusOK, nil
}
func BasicCredentials(r *http.Request) (string, []byte, error) {

View File

@ -0,0 +1,51 @@
package databag
import (
"testing"
"strconv"
"github.com/stretchr/testify/assert"
)
func AddTestContacts(t *testing.T, prefix string, count int) []string {
var access []string
app := AppData{
Name: "Appy",
Description: "A test app",
Url: "http://app.example.com",
};
for i := 0; i < count; i++ {
var token string
var login = prefix + strconv.Itoa(i) + ":pass"
// get account token
r, w, _ := NewRequest("POST", "/admin/accounts", nil)
SetBasicAuth(r, "admin:pass")
AddNodeAccount(w, r)
assert.NoError(t, ReadResponse(w, &token))
// set account profile
r, w, _ = NewRequest("GET", "/account/profile", nil)
SetBearerAuth(r, token);
SetCredentials(r, login)
AddAccount(w, r)
assert.NoError(t, ReadResponse(w, nil))
// acquire new token for attaching app
r, w, _ = NewRequest("POST", "/account/apps", nil)
SetBasicAuth(r, login);
AddAccountApp(w, r);
assert.NoError(t, ReadResponse(w, &token))
// attach app with token
r, w, _ = NewRequest("PUT", "/account/apps", &app)
SetBearerAuth(r, token)
SetAccountApp(w, r)
assert.NoError(t, ReadResponse(w, &token))
access = append(access, token)
}
return access
}

View File

@ -85,12 +85,14 @@ type CardData struct {
}
type CardProfile struct {
Guid string `json:"guid"`
Handle string `json:"handle,omitempty"`
Name string `json:"name,omitempty"`
Description string `json:"description,omitempty"`
Location string `json:"location,omitempty"`
Revision int64 `json:"revision,omitempty"`
ImageSet bool `json:"imageSet,omitempty"`
Version string `json:"version"`
Node string `json:"node"`
}

View File

@ -115,19 +115,24 @@ type Label struct {
type Card struct {
ID uint `gorm:"primaryKey;not null;unique;autoIncrement"`
CardId string `gorm:"not null;index:card,unique"`
AccountID uint `gorm:"not null;index:card,unique"`
GUID string `gorm:"not null"`
AccountID uint `gorm:"not null;index:card,unique;index:guid,unqiue"`
Guid string `gorm:"not null;index:guid,unique"`
Username string
Name string
Description string
Location string
Revision uint64 `gorm:"not null"`
Image string
Version string `gorm:"not null"`
Node string `gorm:"not null"`
ProfileRevision int64 `gorm:"not null"`
Status string `gorm:"not null"`
Token string
Notes string
DataRevision int64 `gorm:"not null"`
Created int64 `gorm:"autoCreateTime"`
Updated int64 `gorm:"autoUpdateTime"`
RemoteProfile int64
RemoteContent int64
Account Account
Groups []Group `gorm:"many2many:card_groups;"`
}

View File

@ -7,81 +7,26 @@ import (
func TestConnectContact(t *testing.T) {
app := AppData{
Name: "Appy",
Description: "A test app",
Url: "http://app.example.com",
};
var token string
// get account token
r, w, _ := NewRequest("POST", "/admin/accounts", nil)
SetBasicAuth(r, "admin:pass")
AddNodeAccount(w, r)
assert.NoError(t, ReadResponse(w, &token))
// set account profile
r, w, _ = NewRequest("GET", "/account/profile", nil)
SetBearerAuth(r, token);
SetCredentials(r, "connecta:pass")
AddAccount(w, r)
assert.NoError(t, ReadResponse(w, nil))
// acquire new token for attaching app
r, w, _ = NewRequest("POST", "/account/apps", nil)
SetBasicAuth(r, "attachapp:pass");
AddAccountApp(w, r);
assert.NoError(t, ReadResponse(w, &token))
// attach app with token
r, w, _ = NewRequest("PUT", "/account/apps", &app)
SetBearerAuth(r, token)
SetAccountApp(w, r)
var aToken string
assert.NoError(t, ReadResponse(w, &aToken))
// get account token
r, w, _ = NewRequest("POST", "/admin/accounts", nil)
SetBasicAuth(r, "admin:pass")
AddNodeAccount(w, r)
assert.NoError(t, ReadResponse(w, &token))
// set account profile
r, w, _ = NewRequest("GET", "/account/profile", nil)
SetBearerAuth(r, token);
SetCredentials(r, "connectb:pass")
AddAccount(w, r)
assert.NoError(t, ReadResponse(w, nil))
// acquire new token for attaching app
r, w, _ = NewRequest("POST", "/account/apps", nil)
SetBasicAuth(r, "connectb:pass");
AddAccountApp(w, r);
assert.NoError(t, ReadResponse(w, &token))
// attach app with token
r, w, _ = NewRequest("PUT", "/account/apps", &app)
SetBearerAuth(r, token)
SetAccountApp(w, r)
var bToken string
assert.NoError(t, ReadResponse(w, &bToken))
// create some contacts for this test
access := AddTestContacts(t, "connect", 2)
// get B identity message
r, w, _ = NewRequest("GET", "/profile/message", nil)
SetBearerAuth(r, bToken)
r, w, _ := NewRequest("GET", "/profile/message", nil)
SetBearerAuth(r, access[0])
GetProfileMessage(w, r)
var msg DataMessage
assert.NoError(t, ReadResponse(w, &msg))
var identity Identity
guid, messageType, ts, err := ReadDataMessage(&msg, &identity)
// add B card in A
r, w, _ = NewRequest("POST", "/contact/cards", &msg)
SetBearerAuth(r, access[1])
AddCard(w, r)
var card Card
assert.NoError(t, ReadResponse(w, &card))
PrintMsg(msg)
PrintMsg(guid)
PrintMsg(messageType)
PrintMsg(ts)
PrintMsg(err)
PrintMsg(identity)
PrintMsg(card)
// A request B
// set B card in A