tavern/web/handler_group.go

186 lines
4.5 KiB
Go

package web
import (
"bytes"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"fmt"
"net/http"
"time"
"github.com/gin-gonic/gin"
"github.com/ngerakines/tavern/common"
"github.com/ngerakines/tavern/errors"
"github.com/ngerakines/tavern/storage"
)
func (h handler) manageGroups(c *gin.Context) {
data, user, session, cont := h.loggedIn(c, true)
if !cont {
return
}
ctx := c.Request.Context()
groups, err := h.storage.GroupActorsForMemberActorRowID(ctx, user.ActorID)
if err != nil {
h.hardFail(c, err)
return
}
data["groups"] = groups
data["following"] = []interface{}{}
data["pending_following"] = []interface{}{}
data["invitations"] = []interface{}{}
if err := session.Save(); err != nil {
h.hardFail(c, errors.NewCannotSaveSessionError(err))
return
}
c.HTML(http.StatusOK, "groups", data)
}
func (h handler) dashboardGroupsCreate(c *gin.Context) {
_, localUser, _, cont := h.loggedIn(c, true)
if !cont {
return
}
ctx := c.Request.Context()
name := c.PostForm("name")
now := time.Now().UTC()
publishedAt := now.Format("2006-01-02T15:04:05Z")
txErr := storage.TransactionalStorage(ctx, h.storage, func(tx storage.Storage) error {
localUserActor, err := h.storage.GetActor(ctx, localUser.ActorID)
if err != nil {
return err
}
available, err := tx.GroupNameAvailable(ctx, name)
if err != nil {
return err
}
if !available {
return fmt.Errorf("name not available")
}
key, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return err
}
privateKeyBytes := x509.MarshalPKCS1PrivateKey(key)
var privateKeyBuffer bytes.Buffer
if err := pem.Encode(&privateKeyBuffer, &pem.Block{
Type: "PRIVATE KEY",
Bytes: privateKeyBytes,
}); err != nil {
return err
}
privateKey := string(privateKeyBuffer.Bytes())
publicKeyBytes, err := x509.MarshalPKIXPublicKey(key.Public())
if err != nil {
return err
}
var publicKeyBuffer bytes.Buffer
if err = pem.Encode(&publicKeyBuffer, &pem.Block{
Type: "PUBLIC KEY",
Bytes: publicKeyBytes,
}); err != nil {
return err
}
publicKey := string(publicKeyBuffer.Bytes())
groupActor := storage.ActorFromGroupInfo(name, name, h.domain, publicKey, key)
groupActorID, _ := storage.JSONString(groupActor, "id")
groupKeyID, _ := storage.JSONDeepString(groupActor, "publicKey", "id")
err = tx.CreateActor(ctx, groupActorID, groupActor)
if err != nil {
return err
}
actorRowID, err := tx.ActorRowIDForActorID(ctx, groupActorID)
if err != nil {
return err
}
groupRowID, err := tx.RecordGroup(ctx, localUser.ID, actorRowID, name, publicKey, privateKey, name, "")
if err != nil {
return err
}
err = tx.UpdateGroupAcceptFollowers(ctx, groupRowID, h.groupConfig.AllowAutoAcceptGroupFollowers)
if err != nil {
return err
}
err = tx.UpdateGroupAllowRemote(ctx, groupRowID, h.groupConfig.AllowRemoteGroupFollowers)
if err != nil {
return err
}
err = tx.UpdateGroupDefaultRole(ctx, groupRowID, storage.GroupRole(h.groupConfig.DefaultGroupMemberRole))
if err != nil {
return err
}
err = tx.UpdateGroupDirectoryOptIn(ctx, groupRowID, h.groupConfig.DefaultDirectoryOptIn)
if err != nil {
return err
}
err = tx.RecordActorKey(ctx, actorRowID, groupKeyID, publicKey)
if err != nil {
return err
}
if err = tx.RecordActorAlias(ctx, actorRowID, fmt.Sprintf("acct:%s@%s", name, h.domain), storage.ActorAliasSubject); err != nil {
return err
}
if err = tx.RecordActorAlias(ctx, actorRowID, groupActorID, storage.ActorAliasSelf); err != nil {
return err
}
followGroup := storage.EmptyPayload()
followGroup["@context"] = "https://www.w3.org/ns/activitystreams"
followGroup["id"] = common.ActivityURL(h.domain, storage.NewV4())
followGroup["summary"] = ""
followGroup["type"] = "Follow"
followGroup["actor"] = localUserActor.ActorID
followGroup["to"] = []string{localUserActor.ActorID}
followGroup["object"] = groupActorID
followGroup["published"] = publishedAt
_, err = tx.RecordGroupMember(ctx, actorRowID, localUser.ActorID, followGroup, storage.AcceptRelationshipStatus, storage.GroupOwner)
if err != nil {
return err
}
err = tx.CreatePendingFollowing(ctx, localUser.ID, actorRowID, followGroup)
if err != nil {
return err
}
err = tx.UpdateFollowingAccepted(ctx, localUser.ID, actorRowID)
if err != nil {
return err
}
return nil
})
if txErr != nil {
h.flashErrorOrFail(c, h.url("groups"), txErr)
return
}
c.Redirect(http.StatusFound, h.url("groups"))
}