feat(app): profile access control & refinements

master
Xeronith 2022-08-22 23:12:12 +04:30
rodzic 5f0929e0ae
commit 166f904b29
9 zmienionych plików z 111 dodań i 51 usunięć

Wyświetl plik

@ -19,6 +19,20 @@ type Note struct {
Content string `json:"content"`
}
func NewNote(from, to, content string) *Note {
return &Note{
Context: "https://www.w3.org/ns/activitystreams",
To: []string{to},
Content: content,
Type: "Note",
AttributedTo: from,
}
}
func NewPublicNote(from, content string) *Note {
return NewNote(from, "https://www.w3.org/ns/activitystreams#Public", content)
}
func (note *Note) Wrap(username string) *Activity {
return &Activity{
Context: ActivityStreams,

Wyświetl plik

@ -15,13 +15,7 @@ func main() {
storage := db.CreateStorage(db.SqliteStorage)
storage.Connect(config.SQLITE_DB)
storage.Migrate(
&repos.User{},
&repos.IncomingActivity{},
&repos.OutgoingActivity{},
&repos.Follower{},
&repos.Following{},
)
storage.Migrate(repos.All...)
app := server.New()
app.SetStorageProvider(storage)

Wyświetl plik

@ -0,0 +1,9 @@
package repos
var All = []interface{}{
&User{},
&IncomingActivity{},
&OutgoingActivity{},
&Follower{},
&Following{},
}

Wyświetl plik

@ -6,6 +6,13 @@ import (
"gorm.io/gorm"
)
type Access int64
const (
ACCESS_PUBLIC Access = iota
ACCESS_PRIVATE
)
// User struct defines the user
type User struct {
gorm.Model
@ -20,6 +27,7 @@ type User struct {
PublicKey string
Avatar string
Banner string
Access Access
}
// CreateUser create a user entry in the user's table

Wyświetl plik

@ -9,9 +9,11 @@ import (
// Follower struct defines a follower
type Follower struct {
gorm.Model
Target string `gorm:"not null"`
Handle string `gorm:"not null"`
Accepted bool `gorm:"not null"`
Target string `gorm:"not null"`
Handle string `gorm:"not null"`
HandleInbox string
Activity string
Accepted bool
}
// CreateFollower creates a new entry in the followers's table
@ -24,6 +26,16 @@ func FindFollowers(dest interface{}, userIden interface{}) *gorm.DB {
return db.Executor.Model(&Follower{}).Find(dest, "`target` = ?", userIden)
}
// FindFollower searches the follower's table with the condition given
func FindFollower(dest interface{}, conds ...interface{}) *gorm.DB {
return db.Executor.Model(&Follower{}).Take(dest, conds...)
}
// FindFollowerById searches the followers's table with the id given
func FindFollowerById(dest interface{}, id uint64) *gorm.DB {
return FindFollower(dest, "id = ?", id)
}
// AcceptFollower accepts a follow request
func AcceptFollower(id interface{}) *gorm.DB {
return db.Executor.Model(&Follower{}).Where("id = ?", id).Update("accepted", true)

Wyświetl plik

@ -6,7 +6,12 @@ import (
"app/models/types"
"config"
. "contracts"
"encoding/json"
"errors"
"server/route"
"strconv"
"gorm.io/gorm"
)
var Followers = route.New(HttpGet, "/u/:username/followers", func(x IContext) error {
@ -39,10 +44,39 @@ var Followers = route.New(HttpGet, "/u/:username/followers", func(x IContext) er
})
var AcceptFollowRequest = route.New(HttpPut, "/u/:username/followers/:id/accept", func(x IContext) error {
username := x.Request().Params("username")
id := x.Request().Params("id")
err := repos.AcceptFollower(id).Error
followerId, err := strconv.ParseUint(id, 10, 64)
if err != nil {
return x.BadRequest("invalid_id")
}
follower := &repos.Follower{}
if err := repos.FindFollowerById(follower, followerId).Error; err != nil {
return x.InternalServerError(err.Error())
}
data, _ := json.Marshal(&activitypub.Activity{
Context: "https://www.w3.org/ns/activitystreams",
ID: x.StringUtil().Format("%s://%s/%s", config.PROTOCOL, config.DOMAIN, x.GUID()),
Type: activitypub.TypeAccept,
Actor: x.StringUtil().Format("%s://%s/u/%s", config.PROTOCOL, config.DOMAIN, username),
Object: follower.Activity,
})
user := &repos.User{}
err = repos.FindUserByUsername(user, username).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
return x.NotFound("No record found for %s.", username)
}
keyId := x.StringUtil().Format("%s://%s/u/%s#main-key", config.PROTOCOL, config.DOMAIN, username)
if err := x.PostActivityStream(follower.HandleInbox, keyId, user.PrivateKey, data, nil); err != nil {
return x.InternalServerError(err.Error())
}
if err := repos.AcceptFollower(follower.ID).Error; err != nil {
return x.InternalServerError(err.Error())
}

Wyświetl plik

@ -23,8 +23,8 @@ var InboxPost = route.New(HttpPost, "/u/:username/inbox", func(x IContext) error
return x.BadRequest("Bad request")
}
key := &types.KeyResponse{}
err := repos.FindUserByUsername(key, username).Error
user := &repos.User{}
err := repos.FindUserByUsername(user, username).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
return x.NotFound("No record found for %s.", username)
}
@ -40,19 +40,35 @@ var InboxPost = route.New(HttpPost, "/u/:username/inbox", func(x IContext) error
}
url := activity.Actor
follower := activity.Actor
var inbox string
{
actor := &activitypub.Actor{}
if err := x.GetActivityStream(url, keyId, key.PrivateKey, nil, actor); err != nil {
if err := x.GetActivityStream(url, keyId, user.PrivateKey, nil, actor); err != nil {
return x.InternalServerError(err.Error())
}
inbox = actor.Inbox
}
{
data, err := json.Marshal(activity)
if err != nil {
return x.InternalServerError(err.Error())
}
follower := &repos.Follower{
Target: x.StringUtil().Format("%s://%s/u/%s", config.PROTOCOL, config.DOMAIN, username),
Handle: activity.Actor,
HandleInbox: inbox,
Activity: string(data),
Accepted: false,
}
if err := repos.CreateFollower(follower); err.Error != nil {
return x.Conflict(err.Error.Error())
}
if user.Access == repos.ACCESS_PUBLIC {
data, _ := json.Marshal(&activitypub.Activity{
Context: "https://www.w3.org/ns/activitystreams",
ID: x.StringUtil().Format("%s://%s/%s", config.PROTOCOL, config.DOMAIN, x.GUID()),
@ -61,16 +77,13 @@ var InboxPost = route.New(HttpPost, "/u/:username/inbox", func(x IContext) error
Object: activity,
})
if err := x.PostActivityStream(inbox, keyId, key.PrivateKey, data, nil); err != nil {
if err := x.PostActivityStream(inbox, keyId, user.PrivateKey, data, nil); err != nil {
return x.InternalServerError(err.Error())
}
if err := repos.CreateFollower(&repos.Follower{
Target: x.StringUtil().Format("%s://%s/u/%s", config.PROTOCOL, config.DOMAIN, username),
Handle: follower,
Accepted: true,
}); err.Error != nil {
return x.Conflict(err.Error.Error())
err := repos.AcceptFollower(follower.ID).Error
if err != nil {
return x.InternalServerError(err.Error())
}
}
@ -127,16 +140,7 @@ var InboxGet = route.New(HttpGet, "/u/:username/inbox", func(x IContext) error {
items := []*activitypub.Activity{}
for _, message := range *messages {
note := &activitypub.Note{
Context: "https://www.w3.org/ns/activitystreams",
To: []string{
"https://www.w3.org/ns/activitystreams#Public",
},
Content: message.Content,
Type: "Note",
AttributedTo: message.From,
}
note := activitypub.NewPublicNote(message.From, message.Content)
activity := note.Wrap(username)
items = append(items, activity)
}

Wyświetl plik

@ -89,16 +89,7 @@ var OutboxGet = route.New(HttpGet, "/u/:username/outbox", func(x IContext) error
items := []*activitypub.Activity{}
for _, message := range *messages {
note := &activitypub.Note{
Context: "https://www.w3.org/ns/activitystreams",
To: []string{
"https://www.w3.org/ns/activitystreams#Public",
},
Content: message.Content,
Type: "Note",
AttributedTo: actor,
}
note := activitypub.NewPublicNote(actor, message.Content)
activity := note.Wrap(username)
items = append(items, activity)
}

Wyświetl plik

@ -19,13 +19,7 @@ func TestMain(m *testing.M) {
storage := db.CreateStorage(db.SqliteStorage)
storage.Connect(config.SQLITE_DB)
storage.Migrate(
&repos.User{},
&repos.IncomingActivity{},
&repos.OutgoingActivity{},
&repos.Follower{},
&repos.Following{},
)
storage.Migrate(repos.All...)
app := server.New()
app.SetStorageProvider(storage)