mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-02-11 22:49:21 -06:00
groupware: add identities of all accounts to the index resource
This commit is contained in:
@@ -2,8 +2,10 @@ package jmap
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
|
||||
"github.com/opencloud-eu/opencloud/pkg/log"
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
// https://jmap.io/spec-mail.html#identityget
|
||||
@@ -20,3 +22,49 @@ func (j *Client) GetIdentity(accountId string, session *Session, ctx context.Con
|
||||
return response, simpleError(err, JmapErrorInvalidJmapResponsePayload)
|
||||
})
|
||||
}
|
||||
|
||||
type IdentitiesGetResponse struct {
|
||||
State string `json:"state"`
|
||||
Identities map[string][]Identity `json:"identities,omitempty"`
|
||||
NotFound []string `json:"notFound,omitempty"`
|
||||
}
|
||||
|
||||
func (j *Client) GetIdentities(accountIds []string, session *Session, ctx context.Context, logger *log.Logger) (IdentitiesGetResponse, Error) {
|
||||
uniqueAccountIds := uniq(accountIds)
|
||||
|
||||
logger = j.loggerParams("", "GetIdentities", session, logger, func(l zerolog.Context) zerolog.Context {
|
||||
return l.Array(logAccountId, logstrarray(uniqueAccountIds))
|
||||
})
|
||||
|
||||
calls := make([]Invocation, len(uniqueAccountIds))
|
||||
for i, accountId := range uniqueAccountIds {
|
||||
calls[i] = invocation(IdentityGet, IdentityGetCommand{AccountId: accountId}, strconv.Itoa(i))
|
||||
}
|
||||
|
||||
cmd, err := request(calls...)
|
||||
if err != nil {
|
||||
return IdentitiesGetResponse{}, SimpleError{code: JmapErrorInvalidJmapRequestPayload, err: err}
|
||||
}
|
||||
return command(j.api, logger, ctx, session, j.onSessionOutdated, cmd, func(body *Response) (IdentitiesGetResponse, Error) {
|
||||
identities := make(map[string][]Identity, len(uniqueAccountIds))
|
||||
lastState := ""
|
||||
notFound := []string{}
|
||||
for i, accountId := range uniqueAccountIds {
|
||||
var response IdentityGetResponse
|
||||
err = retrieveResponseMatchParameters(body, IdentityGet, strconv.Itoa(i), &response)
|
||||
if err != nil {
|
||||
return IdentitiesGetResponse{}, simpleError(err, JmapErrorInvalidJmapResponsePayload)
|
||||
} else {
|
||||
identities[accountId] = response.List
|
||||
}
|
||||
lastState = response.State
|
||||
notFound = append(notFound, response.NotFound...)
|
||||
}
|
||||
|
||||
return IdentitiesGetResponse{
|
||||
Identities: identities,
|
||||
State: lastState,
|
||||
NotFound: uniq(notFound),
|
||||
}, nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1568,7 +1568,7 @@ type IdentityGetResponse struct {
|
||||
AccountId string `json:"accountId"`
|
||||
State string `json:"state"`
|
||||
List []Identity `json:"list,omitempty"`
|
||||
NotFound []any `json:"notFound,omitempty"`
|
||||
NotFound []string `json:"notFound,omitempty"`
|
||||
}
|
||||
|
||||
type VacationResponseGetCommand struct {
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/opencloud-eu/opencloud/pkg/log"
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
type eventListeners[T any] struct {
|
||||
@@ -222,3 +223,47 @@ func (i *Invocation) UnmarshalJSON(bs []byte) error {
|
||||
i.Parameters = params
|
||||
return nil
|
||||
}
|
||||
|
||||
const logMaxStrLength = 1024
|
||||
|
||||
// Safely caps a string to a given size to avoid log bombing.
|
||||
// Use this function to wrap strings that are user input (HTTP headers, path parameters, URI parameters, HTTP body, ...).
|
||||
func logstr(text string) string {
|
||||
runes := []rune(text)
|
||||
|
||||
if len(runes) <= logMaxStrLength {
|
||||
return text
|
||||
} else {
|
||||
return string(runes[0:logMaxStrLength-1]) + `\u2026` // hellip
|
||||
}
|
||||
}
|
||||
|
||||
type SafeLogStringArrayMarshaller struct {
|
||||
array []string
|
||||
}
|
||||
|
||||
func (m SafeLogStringArrayMarshaller) MarshalZerologArray(a *zerolog.Array) {
|
||||
for _, elem := range m.array {
|
||||
a.Str(logstr(elem))
|
||||
}
|
||||
}
|
||||
|
||||
var _ zerolog.LogArrayMarshaler = SafeLogStringArrayMarshaller{}
|
||||
|
||||
func logstrarray(array []string) SafeLogStringArrayMarshaller {
|
||||
return SafeLogStringArrayMarshaller{array: array}
|
||||
}
|
||||
|
||||
func uniq[T comparable](ary []T) []T {
|
||||
m := map[T]bool{}
|
||||
for _, v := range ary {
|
||||
m[v] = true
|
||||
}
|
||||
set := make([]T, len(m))
|
||||
i := 0
|
||||
for v := range m {
|
||||
set[i] = v
|
||||
i++
|
||||
}
|
||||
return set
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ package groupware
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/opencloud-eu/opencloud/pkg/jmap"
|
||||
)
|
||||
|
||||
type IndexLimits struct {
|
||||
@@ -37,6 +39,7 @@ type IndexAccount struct {
|
||||
IsPersonal bool `json:"isPersonal"`
|
||||
IsReadOnly bool `json:"isReadOnly"`
|
||||
Capabilities IndexAccountCapabilities `json:"capabilities"`
|
||||
Identities []jmap.Identity `json:"identities,omitempty"`
|
||||
}
|
||||
|
||||
type IndexPrimaryAccounts struct {
|
||||
@@ -69,28 +72,46 @@ type SwaggerIndexResponse struct {
|
||||
// 200: IndexResponse
|
||||
func (g Groupware) Index(w http.ResponseWriter, r *http.Request) {
|
||||
g.respond(w, r, func(req Request) Response {
|
||||
|
||||
accountIds := make([]string, len(req.session.Accounts))
|
||||
i := 0
|
||||
for k := range req.session.Accounts {
|
||||
accountIds[i] = k
|
||||
i++
|
||||
}
|
||||
accountIds = uniq(accountIds)
|
||||
|
||||
identitiesResponse, err := g.jmap.GetIdentities(accountIds, req.session, req.ctx, req.logger)
|
||||
if err != nil {
|
||||
return req.errorResponseFromJmap(err)
|
||||
}
|
||||
|
||||
accounts := make(map[string]IndexAccount, len(req.session.Accounts))
|
||||
for i, a := range req.session.Accounts {
|
||||
accounts[i] = IndexAccount{
|
||||
Name: a.Name,
|
||||
IsPersonal: a.IsPersonal,
|
||||
IsReadOnly: a.IsReadOnly,
|
||||
for accountId, account := range req.session.Accounts {
|
||||
indexAccount := IndexAccount{
|
||||
Name: account.Name,
|
||||
IsPersonal: account.IsPersonal,
|
||||
IsReadOnly: account.IsReadOnly,
|
||||
Capabilities: IndexAccountCapabilities{
|
||||
Mail: IndexAccountMailCapabilities{
|
||||
MaxMailboxDepth: a.AccountCapabilities.Mail.MaxMailboxDepth,
|
||||
MaxSizeMailboxName: a.AccountCapabilities.Mail.MaxSizeMailboxName,
|
||||
MaxSizeAttachmentsPerEmail: a.AccountCapabilities.Mail.MaxSizeAttachmentsPerEmail,
|
||||
MayCreateTopLevelMailbox: a.AccountCapabilities.Mail.MayCreateTopLevelMailbox,
|
||||
MaxDelayedSend: a.AccountCapabilities.Submission.MaxDelayedSend,
|
||||
MaxMailboxDepth: account.AccountCapabilities.Mail.MaxMailboxDepth,
|
||||
MaxSizeMailboxName: account.AccountCapabilities.Mail.MaxSizeMailboxName,
|
||||
MaxSizeAttachmentsPerEmail: account.AccountCapabilities.Mail.MaxSizeAttachmentsPerEmail,
|
||||
MayCreateTopLevelMailbox: account.AccountCapabilities.Mail.MayCreateTopLevelMailbox,
|
||||
MaxDelayedSend: account.AccountCapabilities.Submission.MaxDelayedSend,
|
||||
},
|
||||
Sieve: IndexAccountSieveCapabilities{
|
||||
MaxSizeScriptName: a.AccountCapabilities.Sieve.MaxSizeScript,
|
||||
MaxSizeScript: a.AccountCapabilities.Sieve.MaxSizeScript,
|
||||
MaxNumberScripts: a.AccountCapabilities.Sieve.MaxNumberScripts,
|
||||
MaxNumberRedirects: a.AccountCapabilities.Sieve.MaxNumberRedirects,
|
||||
MaxSizeScriptName: account.AccountCapabilities.Sieve.MaxSizeScript,
|
||||
MaxSizeScript: account.AccountCapabilities.Sieve.MaxSizeScript,
|
||||
MaxNumberScripts: account.AccountCapabilities.Sieve.MaxNumberScripts,
|
||||
MaxNumberRedirects: account.AccountCapabilities.Sieve.MaxNumberRedirects,
|
||||
},
|
||||
},
|
||||
}
|
||||
if identity, ok := identitiesResponse.Identities[accountId]; ok {
|
||||
indexAccount.Identities = identity
|
||||
}
|
||||
accounts[accountId] = indexAccount
|
||||
}
|
||||
|
||||
return response(IndexResponse{
|
||||
|
||||
@@ -574,3 +574,17 @@ func (g Groupware) NotFound(w http.ResponseWriter, r *http.Request) {
|
||||
render.Status(r, http.StatusNotFound)
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
}
|
||||
|
||||
func uniq[T comparable](ary []T) []T {
|
||||
m := map[T]bool{}
|
||||
for _, v := range ary {
|
||||
m[v] = true
|
||||
}
|
||||
set := make([]T, len(m))
|
||||
i := 0
|
||||
for v := range m {
|
||||
set[i] = v
|
||||
i++
|
||||
}
|
||||
return set
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user