mirror of
https://github.com/hanzoai/team.git
synced 2026-09-30 01:57:35 +00:00
294 lines
9.4 KiB
Go
294 lines
9.4 KiB
Go
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
|
|
package api4
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"strconv"
|
|
|
|
"github.com/mattermost/mattermost/server/public/model"
|
|
"github.com/mattermost/mattermost/server/public/shared/mlog"
|
|
)
|
|
|
|
// initChannelJoinRequestRoutes registers the discoverable-private-channel
|
|
// join request endpoints. The route group is split into its own file so the
|
|
// handlers stay isolated from the rest of api4/channel.go.
|
|
func (api *API) initChannelJoinRequestRoutes() {
|
|
if !api.srv.Config().FeatureFlags.DiscoverableChannels {
|
|
return
|
|
}
|
|
|
|
api.BaseRoutes.Channel.Handle("/join_request", api.APISessionRequired(requestJoinChannel)).Methods(http.MethodPost)
|
|
api.BaseRoutes.Channel.Handle("/join_request", api.APISessionRequired(getMyChannelJoinRequest)).Methods(http.MethodGet)
|
|
api.BaseRoutes.Channel.Handle("/join_request", api.APISessionRequired(withdrawMyChannelJoinRequest)).Methods(http.MethodDelete)
|
|
|
|
api.BaseRoutes.Channel.Handle("/join_requests", api.APISessionRequired(getChannelJoinRequests)).Methods(http.MethodGet)
|
|
api.BaseRoutes.Channel.Handle("/join_requests/count", api.APISessionRequired(countPendingChannelJoinRequests)).Methods(http.MethodGet)
|
|
api.BaseRoutes.Channel.Handle("/join_requests/{request_id:[A-Za-z0-9]+}", api.APISessionRequired(patchChannelJoinRequest)).Methods(http.MethodPatch)
|
|
|
|
api.BaseRoutes.User.Handle("/channel_join_requests", api.APISessionRequired(getMyChannelJoinRequests)).Methods(http.MethodGet)
|
|
}
|
|
|
|
// channelJoinRequestBody is the POST body shape for /channels/{id}/join_request.
|
|
type channelJoinRequestBody struct {
|
|
Message string `json:"message"`
|
|
}
|
|
|
|
func requireDiscoverableChannelsEnabled(c *Context, where string) bool {
|
|
if !c.App.Config().FeatureFlags.DiscoverableChannels {
|
|
c.Err = model.NewAppError(where, "api.channel.discoverable_join_request.feature_disabled.app_error", nil, "", http.StatusNotFound)
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
func requestJoinChannel(c *Context, w http.ResponseWriter, r *http.Request) {
|
|
c.RequireChannelId()
|
|
if c.Err != nil {
|
|
return
|
|
}
|
|
if !requireDiscoverableChannelsEnabled(c, "requestJoinChannel") {
|
|
return
|
|
}
|
|
|
|
var body channelJoinRequestBody
|
|
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
|
|
c.SetInvalidParamWithErr("body", err)
|
|
return
|
|
}
|
|
|
|
auditRec := c.MakeAuditRecord(model.AuditEventCreateChannelJoinRequest, model.AuditStatusFail)
|
|
defer c.LogAuditRec(auditRec)
|
|
model.AddEventParameterToAuditRec(auditRec, "channel_id", c.Params.ChannelId)
|
|
model.AddEventParameterToAuditRec(auditRec, "user_id", c.AppContext.Session().UserId)
|
|
|
|
joined, req, appErr := c.App.RequestJoinChannel(c.AppContext, c.AppContext.Session().UserId, c.Params.ChannelId, body.Message)
|
|
if appErr != nil {
|
|
c.Err = appErr
|
|
return
|
|
}
|
|
|
|
auditRec.Success()
|
|
if req != nil {
|
|
auditRec.AddEventResultState(req)
|
|
}
|
|
|
|
if joined {
|
|
// Mirror the membership endpoint's "no body, just status" semantics
|
|
// when the user was added directly via the ABAC fast path.
|
|
w.WriteHeader(http.StatusCreated)
|
|
if err := json.NewEncoder(w).Encode(map[string]string{"status": model.ChannelJoinRequestStatusApproved}); err != nil {
|
|
c.Logger.Warn("Error while writing response", mlog.Err(err))
|
|
}
|
|
return
|
|
}
|
|
|
|
w.WriteHeader(http.StatusCreated)
|
|
if err := json.NewEncoder(w).Encode(req); err != nil {
|
|
c.Logger.Warn("Error while writing response", mlog.Err(err))
|
|
}
|
|
}
|
|
|
|
func getMyChannelJoinRequest(c *Context, w http.ResponseWriter, r *http.Request) {
|
|
c.RequireChannelId()
|
|
if c.Err != nil {
|
|
return
|
|
}
|
|
if !requireDiscoverableChannelsEnabled(c, "getMyChannelJoinRequest") {
|
|
return
|
|
}
|
|
|
|
req, appErr := c.App.GetMyChannelJoinRequest(c.AppContext, c.AppContext.Session().UserId, c.Params.ChannelId)
|
|
if appErr != nil {
|
|
c.Err = appErr
|
|
return
|
|
}
|
|
|
|
if req == nil {
|
|
// Mirror REST conventions: not-found instead of an explicit `null`
|
|
// so clients can distinguish "no pending request" from "service down".
|
|
w.WriteHeader(http.StatusNotFound)
|
|
return
|
|
}
|
|
|
|
if err := json.NewEncoder(w).Encode(req); err != nil {
|
|
c.Logger.Warn("Error while writing response", mlog.Err(err))
|
|
}
|
|
}
|
|
|
|
func withdrawMyChannelJoinRequest(c *Context, w http.ResponseWriter, r *http.Request) {
|
|
c.RequireChannelId()
|
|
if c.Err != nil {
|
|
return
|
|
}
|
|
if !requireDiscoverableChannelsEnabled(c, "withdrawMyChannelJoinRequest") {
|
|
return
|
|
}
|
|
|
|
auditRec := c.MakeAuditRecord(model.AuditEventWithdrawChannelJoinRequest, model.AuditStatusFail)
|
|
defer c.LogAuditRec(auditRec)
|
|
model.AddEventParameterToAuditRec(auditRec, "channel_id", c.Params.ChannelId)
|
|
model.AddEventParameterToAuditRec(auditRec, "user_id", c.AppContext.Session().UserId)
|
|
|
|
req, appErr := c.App.GetMyChannelJoinRequest(c.AppContext, c.AppContext.Session().UserId, c.Params.ChannelId)
|
|
if appErr != nil {
|
|
c.Err = appErr
|
|
return
|
|
}
|
|
if req == nil {
|
|
c.Err = model.NewAppError("withdrawMyChannelJoinRequest", "app.channel.join_request.not_found.app_error", nil, "channel_id="+c.Params.ChannelId, http.StatusNotFound)
|
|
return
|
|
}
|
|
|
|
updated, appErr := c.App.WithdrawChannelJoinRequest(c.AppContext, req.Id, c.AppContext.Session().UserId)
|
|
if appErr != nil {
|
|
c.Err = appErr
|
|
return
|
|
}
|
|
|
|
auditRec.Success()
|
|
auditRec.AddEventResultState(updated)
|
|
|
|
if err := json.NewEncoder(w).Encode(updated); err != nil {
|
|
c.Logger.Warn("Error while writing response", mlog.Err(err))
|
|
}
|
|
}
|
|
|
|
func getChannelJoinRequests(c *Context, w http.ResponseWriter, r *http.Request) {
|
|
c.RequireChannelId()
|
|
if c.Err != nil {
|
|
return
|
|
}
|
|
if !requireDiscoverableChannelsEnabled(c, "getChannelJoinRequests") {
|
|
return
|
|
}
|
|
|
|
if ok, _ := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionManageChannelJoinRequests); !ok {
|
|
c.SetPermissionError(model.PermissionManageChannelJoinRequests)
|
|
return
|
|
}
|
|
|
|
opts := model.GetChannelJoinRequestsOpts{
|
|
Status: r.URL.Query().Get("status"),
|
|
Page: c.Params.Page,
|
|
PerPage: c.Params.PerPage,
|
|
}
|
|
|
|
list, appErr := c.App.GetChannelJoinRequests(c.AppContext, c.Params.ChannelId, opts)
|
|
if appErr != nil {
|
|
c.Err = appErr
|
|
return
|
|
}
|
|
|
|
if err := json.NewEncoder(w).Encode(list); err != nil {
|
|
c.Logger.Warn("Error while writing response", mlog.Err(err))
|
|
}
|
|
}
|
|
|
|
func countPendingChannelJoinRequests(c *Context, w http.ResponseWriter, r *http.Request) {
|
|
c.RequireChannelId()
|
|
if c.Err != nil {
|
|
return
|
|
}
|
|
if !requireDiscoverableChannelsEnabled(c, "countPendingChannelJoinRequests") {
|
|
return
|
|
}
|
|
|
|
if ok, _ := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionManageChannelJoinRequests); !ok {
|
|
c.SetPermissionError(model.PermissionManageChannelJoinRequests)
|
|
return
|
|
}
|
|
|
|
count, appErr := c.App.CountPendingChannelJoinRequests(c.AppContext, c.Params.ChannelId)
|
|
if appErr != nil {
|
|
c.Err = appErr
|
|
return
|
|
}
|
|
|
|
if err := json.NewEncoder(w).Encode(map[string]int64{"count": count}); err != nil {
|
|
c.Logger.Warn("Error while writing response", mlog.Err(err))
|
|
}
|
|
}
|
|
|
|
func patchChannelJoinRequest(c *Context, w http.ResponseWriter, r *http.Request) {
|
|
c.RequireChannelId()
|
|
if c.Err != nil {
|
|
return
|
|
}
|
|
if !requireDiscoverableChannelsEnabled(c, "patchChannelJoinRequest") {
|
|
return
|
|
}
|
|
if !model.IsValidId(c.Params.RequestId) {
|
|
c.SetInvalidURLParam("request_id")
|
|
return
|
|
}
|
|
|
|
if ok, _ := c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), c.Params.ChannelId, model.PermissionManageChannelJoinRequests); !ok {
|
|
c.SetPermissionError(model.PermissionManageChannelJoinRequests)
|
|
return
|
|
}
|
|
|
|
var patch model.ChannelJoinRequestPatch
|
|
if err := json.NewDecoder(r.Body).Decode(&patch); err != nil {
|
|
c.SetInvalidParamWithErr("channel_join_request_patch", err)
|
|
return
|
|
}
|
|
|
|
auditRec := c.MakeAuditRecord(model.AuditEventUpdateChannelJoinRequest, model.AuditStatusFail)
|
|
defer c.LogAuditRec(auditRec)
|
|
model.AddEventParameterToAuditRec(auditRec, "channel_id", c.Params.ChannelId)
|
|
model.AddEventParameterToAuditRec(auditRec, "request_id", c.Params.RequestId)
|
|
model.AddEventParameterToAuditRec(auditRec, "status", patch.Status)
|
|
// Capture only the presence of a denial reason in the audit log; the
|
|
// free-text contents are intentionally excluded.
|
|
model.AddEventParameterToAuditRec(auditRec, "has_denial_reason", strconv.FormatBool(patch.DenialReason != nil && *patch.DenialReason != ""))
|
|
|
|
updated, appErr := c.App.UpdateChannelJoinRequest(c.AppContext, c.Params.RequestId, c.Params.ChannelId, &patch, c.AppContext.Session().UserId)
|
|
if appErr != nil {
|
|
c.Err = appErr
|
|
return
|
|
}
|
|
|
|
auditRec.Success()
|
|
auditRec.AddEventResultState(updated)
|
|
|
|
if err := json.NewEncoder(w).Encode(updated); err != nil {
|
|
c.Logger.Warn("Error while writing response", mlog.Err(err))
|
|
}
|
|
}
|
|
|
|
func getMyChannelJoinRequests(c *Context, w http.ResponseWriter, r *http.Request) {
|
|
c.RequireUserId()
|
|
if c.Err != nil {
|
|
return
|
|
}
|
|
if !requireDiscoverableChannelsEnabled(c, "getMyChannelJoinRequests") {
|
|
return
|
|
}
|
|
|
|
// Only the calling user can list their own requests; admins should use
|
|
// the per-channel queue endpoint.
|
|
if c.Params.UserId != c.AppContext.Session().UserId {
|
|
c.SetPermissionError(model.PermissionEditOtherUsers)
|
|
return
|
|
}
|
|
|
|
opts := model.GetChannelJoinRequestsOpts{
|
|
Status: r.URL.Query().Get("status"),
|
|
Page: c.Params.Page,
|
|
PerPage: c.Params.PerPage,
|
|
}
|
|
|
|
list, appErr := c.App.GetMyChannelJoinRequests(c.AppContext, c.AppContext.Session().UserId, opts)
|
|
if appErr != nil {
|
|
c.Err = appErr
|
|
return
|
|
}
|
|
|
|
if err := json.NewEncoder(w).Encode(list); err != nil {
|
|
c.Logger.Warn("Error while writing response", mlog.Err(err))
|
|
}
|
|
}
|