mirror of https://gitlab.com/ngerakines/tavern.git
186 lines
4.5 KiB
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"))
|
|
}
|