mirror of
https://github.com/mattermost/mattermost.git
synced 2026-02-03 20:40:00 -05:00
Some checks are pending
API / build (push) Waiting to run
Server CI / Compute Go Version (push) Waiting to run
Server CI / Check mocks (push) Blocked by required conditions
Server CI / Check go mod tidy (push) Blocked by required conditions
Server CI / check-style (push) Blocked by required conditions
Server CI / Check serialization methods for hot structs (push) Blocked by required conditions
Server CI / Vet API (push) Blocked by required conditions
Server CI / Check migration files (push) Blocked by required conditions
Server CI / Generate email templates (push) Blocked by required conditions
Server CI / Check store layers (push) Blocked by required conditions
Server CI / Check mmctl docs (push) Blocked by required conditions
Server CI / Postgres with binary parameters (push) Blocked by required conditions
Server CI / Postgres (push) Blocked by required conditions
Server CI / Postgres (FIPS) (push) Blocked by required conditions
Server CI / Generate Test Coverage (push) Blocked by required conditions
Server CI / Run mmctl tests (push) Blocked by required conditions
Server CI / Run mmctl tests (FIPS) (push) Blocked by required conditions
Server CI / Build mattermost server app (push) Blocked by required conditions
Web App CI / check-lint (push) Waiting to run
Web App CI / check-i18n (push) Waiting to run
Web App CI / check-types (push) Waiting to run
Web App CI / test (push) Waiting to run
Web App CI / build (push) Waiting to run
758 lines
24 KiB
Go
758 lines
24 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"
|
|
"strings"
|
|
|
|
"github.com/mattermost/mattermost/server/public/model"
|
|
"github.com/mattermost/mattermost/server/public/shared/mlog"
|
|
)
|
|
|
|
func (api *API) InitAccessControlPolicy() {
|
|
if !api.srv.Config().FeatureFlags.AttributeBasedAccessControl {
|
|
return
|
|
}
|
|
api.BaseRoutes.AccessControlPolicies.Handle("", api.APISessionRequired(createAccessControlPolicy)).Methods(http.MethodPut)
|
|
api.BaseRoutes.AccessControlPolicies.Handle("/search", api.APISessionRequired(searchAccessControlPolicies)).Methods(http.MethodPost)
|
|
api.BaseRoutes.AccessControlPolicies.Handle("/activate", api.APISessionRequired(setActiveStatus)).Methods(http.MethodPut)
|
|
|
|
api.BaseRoutes.AccessControlPolicies.Handle("/cel/check", api.APISessionRequired(checkExpression)).Methods(http.MethodPost)
|
|
api.BaseRoutes.AccessControlPolicies.Handle("/cel/test", api.APISessionRequired(testExpression)).Methods(http.MethodPost)
|
|
api.BaseRoutes.AccessControlPolicies.Handle("/cel/validate_requester", api.APISessionRequired(validateExpressionAgainstRequester)).Methods(http.MethodPost)
|
|
api.BaseRoutes.AccessControlPolicies.Handle("/cel/autocomplete/fields", api.APISessionRequired(getFieldsAutocomplete)).Methods(http.MethodGet)
|
|
api.BaseRoutes.AccessControlPolicies.Handle("/cel/visual_ast", api.APISessionRequired(convertToVisualAST)).Methods(http.MethodPost)
|
|
|
|
api.BaseRoutes.AccessControlPolicy.Handle("", api.APISessionRequired(getAccessControlPolicy)).Methods(http.MethodGet)
|
|
api.BaseRoutes.AccessControlPolicy.Handle("", api.APISessionRequired(deleteAccessControlPolicy)).Methods(http.MethodDelete)
|
|
api.BaseRoutes.AccessControlPolicy.Handle("/activate", api.APISessionRequired(updateActiveStatus)).Methods(http.MethodGet)
|
|
api.BaseRoutes.AccessControlPolicy.Handle("/assign", api.APISessionRequired(assignAccessPolicy)).Methods(http.MethodPost)
|
|
api.BaseRoutes.AccessControlPolicy.Handle("/unassign", api.APISessionRequired(unassignAccessPolicy)).Methods(http.MethodDelete)
|
|
api.BaseRoutes.AccessControlPolicy.Handle("/resources/channels", api.APISessionRequired(getChannelsForAccessControlPolicy)).Methods(http.MethodGet)
|
|
api.BaseRoutes.AccessControlPolicy.Handle("/resources/channels/search", api.APISessionRequired(searchChannelsForAccessControlPolicy)).Methods(http.MethodPost)
|
|
}
|
|
|
|
func createAccessControlPolicy(c *Context, w http.ResponseWriter, r *http.Request) {
|
|
var policy model.AccessControlPolicy
|
|
if jsonErr := json.NewDecoder(r.Body).Decode(&policy); jsonErr != nil {
|
|
c.SetInvalidParamWithErr("policy", jsonErr)
|
|
return
|
|
}
|
|
|
|
auditRec := c.MakeAuditRecord(model.AuditEventCreateAccessControlPolicy, model.AuditStatusFail)
|
|
defer c.LogAuditRec(auditRec)
|
|
model.AddEventParameterAuditableToAuditRec(auditRec, "requested", &policy)
|
|
|
|
switch policy.Type {
|
|
case model.AccessControlPolicyTypeParent:
|
|
if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionManageSystem) {
|
|
c.SetPermissionError(model.PermissionManageSystem)
|
|
return
|
|
}
|
|
case model.AccessControlPolicyTypeChannel:
|
|
// Check if user has system admin permission first
|
|
hasManageSystemPermission := c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionManageSystem)
|
|
|
|
if !hasManageSystemPermission {
|
|
// For non-system admins, check channel-specific permission
|
|
if !model.IsValidId(policy.ID) {
|
|
c.SetInvalidParam("policy.id")
|
|
return
|
|
}
|
|
|
|
hasChannelPermission := c.App.HasPermissionToChannel(c.AppContext, c.AppContext.Session().UserId, policy.ID, model.PermissionManageChannelAccessRules)
|
|
if !hasChannelPermission {
|
|
c.SetPermissionError(model.PermissionManageChannelAccessRules)
|
|
return
|
|
}
|
|
|
|
// Now do the full validation (channel exists, is private, etc.)
|
|
if appErr := c.App.ValidateChannelAccessControlPolicyCreation(c.AppContext, c.AppContext.Session().UserId, &policy); appErr != nil {
|
|
c.Err = appErr
|
|
return
|
|
}
|
|
}
|
|
default:
|
|
c.SetInvalidParam("type")
|
|
return
|
|
}
|
|
|
|
np, appErr := c.App.CreateOrUpdateAccessControlPolicy(c.AppContext, &policy)
|
|
if appErr != nil {
|
|
c.Err = appErr
|
|
return
|
|
}
|
|
|
|
auditRec.Success()
|
|
auditRec.AddEventObjectType("access_control_policy")
|
|
auditRec.AddEventResultState(np)
|
|
|
|
js, err := json.Marshal(np)
|
|
if err != nil {
|
|
c.Err = model.NewAppError("createAccessControlPolicy", "api.marshal_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
|
return
|
|
}
|
|
|
|
if _, err := w.Write(js); err != nil {
|
|
c.Logger.Warn("Error while writing response", mlog.Err(err))
|
|
}
|
|
}
|
|
|
|
func getAccessControlPolicy(c *Context, w http.ResponseWriter, r *http.Request) {
|
|
c.RequirePolicyId()
|
|
if c.Err != nil {
|
|
return
|
|
}
|
|
policyID := c.Params.PolicyId
|
|
|
|
// Extract optional channelId from query parameters for context
|
|
channelID := r.URL.Query().Get("channelId")
|
|
|
|
// Check if user has system admin permission OR channel-specific permission
|
|
hasManageSystemPermission := c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionManageSystem)
|
|
if !hasManageSystemPermission {
|
|
// For non-system admins, validate policy access permission (read-only access for GET requests)
|
|
if appErr := c.App.ValidateAccessControlPolicyPermissionWithChannelContext(c.AppContext, c.AppContext.Session().UserId, policyID, true, channelID); appErr != nil {
|
|
c.SetPermissionError(model.PermissionManageSystem)
|
|
return
|
|
}
|
|
}
|
|
|
|
policy, appErr := c.App.GetAccessControlPolicy(c.AppContext, policyID)
|
|
if appErr != nil {
|
|
c.Err = appErr
|
|
return
|
|
}
|
|
|
|
js, err := json.Marshal(policy)
|
|
if err != nil {
|
|
c.Err = model.NewAppError("getAccessControlPolicy", "api.marshal_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
|
return
|
|
}
|
|
|
|
if _, err := w.Write(js); err != nil {
|
|
c.Logger.Warn("Error while writing response", mlog.Err(err))
|
|
}
|
|
}
|
|
|
|
func deleteAccessControlPolicy(c *Context, w http.ResponseWriter, r *http.Request) {
|
|
c.RequirePolicyId()
|
|
if c.Err != nil {
|
|
return
|
|
}
|
|
policyID := c.Params.PolicyId
|
|
|
|
auditRec := c.MakeAuditRecord(model.AuditEventDeleteAccessControlPolicy, model.AuditStatusFail)
|
|
defer c.LogAuditRec(auditRec)
|
|
model.AddEventParameterToAuditRec(auditRec, "id", policyID)
|
|
|
|
// Check if user has system admin permission OR channel-specific permission
|
|
hasManageSystemPermission := c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionManageSystem)
|
|
if !hasManageSystemPermission {
|
|
// For non-system admins, validate policy access permission
|
|
if appErr := c.App.ValidateAccessControlPolicyPermission(c.AppContext, c.AppContext.Session().UserId, policyID); appErr != nil {
|
|
c.SetPermissionError(model.PermissionManageSystem)
|
|
return
|
|
}
|
|
}
|
|
|
|
appErr := c.App.DeleteAccessControlPolicy(c.AppContext, policyID)
|
|
if appErr != nil {
|
|
c.Err = appErr
|
|
return
|
|
}
|
|
auditRec.Success()
|
|
}
|
|
|
|
func checkExpression(c *Context, w http.ResponseWriter, r *http.Request) {
|
|
// request type reserved for future expansion
|
|
// for now, we only support the expression check
|
|
checkExpressionRequest := struct {
|
|
Expression string `json:"expression"`
|
|
ChannelId string `json:"channelId,omitempty"`
|
|
}{}
|
|
if jsonErr := json.NewDecoder(r.Body).Decode(&checkExpressionRequest); jsonErr != nil {
|
|
c.SetInvalidParamWithErr("user", jsonErr)
|
|
return
|
|
}
|
|
|
|
// Get channelId from request body (required for channel-specific permission check)
|
|
channelId := checkExpressionRequest.ChannelId
|
|
if channelId != "" && !model.IsValidId(channelId) {
|
|
c.SetInvalidParam("channelId")
|
|
return
|
|
}
|
|
|
|
// Check permissions: system admin OR channel-specific permission
|
|
hasSystemPermission := c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionManageSystem)
|
|
if !hasSystemPermission {
|
|
// For channel admins, channelId is required
|
|
if channelId == "" {
|
|
c.SetPermissionError(model.PermissionManageSystem)
|
|
return
|
|
}
|
|
|
|
// SECURE: Check specific channel permission
|
|
hasChannelPermission := c.App.HasPermissionToChannel(c.AppContext, c.AppContext.Session().UserId, channelId, model.PermissionManageChannelAccessRules)
|
|
if !hasChannelPermission {
|
|
c.SetPermissionError(model.PermissionManageChannelAccessRules)
|
|
return
|
|
}
|
|
}
|
|
|
|
errs, appErr := c.App.CheckExpression(c.AppContext, checkExpressionRequest.Expression)
|
|
if appErr != nil {
|
|
c.Err = appErr
|
|
return
|
|
}
|
|
|
|
js, err := json.Marshal(errs)
|
|
if err != nil {
|
|
c.Err = model.NewAppError("checkExpression", "api.marshal_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
|
return
|
|
}
|
|
|
|
if _, err := w.Write(js); err != nil {
|
|
c.Logger.Warn("Error while writing response", mlog.Err(err))
|
|
}
|
|
}
|
|
|
|
func testExpression(c *Context, w http.ResponseWriter, r *http.Request) {
|
|
var checkExpressionRequest model.QueryExpressionParams
|
|
if jsonErr := json.NewDecoder(r.Body).Decode(&checkExpressionRequest); jsonErr != nil {
|
|
c.SetInvalidParamWithErr("user", jsonErr)
|
|
return
|
|
}
|
|
|
|
// Get channelId from request body (required for channel-specific permission check)
|
|
channelId := checkExpressionRequest.ChannelId
|
|
if channelId != "" && !model.IsValidId(channelId) {
|
|
c.SetInvalidParam("channelId")
|
|
return
|
|
}
|
|
|
|
// Check permissions: system admin OR channel-specific permission
|
|
hasSystemPermission := c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionManageSystem)
|
|
if !hasSystemPermission {
|
|
// For channel admins, channelId is required
|
|
if channelId == "" {
|
|
c.SetPermissionError(model.PermissionManageSystem)
|
|
return
|
|
}
|
|
|
|
// SECURE: Check specific channel permission
|
|
hasChannelPermission := c.App.HasPermissionToChannel(c.AppContext, c.AppContext.Session().UserId, channelId, model.PermissionManageChannelAccessRules)
|
|
if !hasChannelPermission {
|
|
c.SetPermissionError(model.PermissionManageChannelAccessRules)
|
|
return
|
|
}
|
|
}
|
|
|
|
var users []*model.User
|
|
var count int64
|
|
var appErr *model.AppError
|
|
|
|
searchOpts := model.SubjectSearchOptions{
|
|
Term: checkExpressionRequest.Term,
|
|
Limit: checkExpressionRequest.Limit,
|
|
Cursor: model.SubjectCursor{
|
|
TargetID: checkExpressionRequest.After,
|
|
},
|
|
}
|
|
|
|
if hasSystemPermission {
|
|
// SYSTEM ADMIN: Can see ALL users (no restrictions)
|
|
users, count, appErr = c.App.TestExpression(c.AppContext, checkExpressionRequest.Expression, searchOpts)
|
|
} else {
|
|
// CHANNEL ADMIN: Only see users matching expressions with attributes they possess
|
|
users, count, appErr = c.App.TestExpressionWithChannelContext(c.AppContext, checkExpressionRequest.Expression, searchOpts)
|
|
}
|
|
|
|
if appErr != nil {
|
|
c.Err = appErr
|
|
return
|
|
}
|
|
|
|
resp := model.AccessControlPolicyTestResponse{
|
|
Users: users,
|
|
Total: count,
|
|
}
|
|
|
|
js, err := json.Marshal(resp)
|
|
if err != nil {
|
|
c.Err = model.NewAppError("checkExpression", "api.marshal_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
|
return
|
|
}
|
|
|
|
if _, err := w.Write(js); err != nil {
|
|
c.Logger.Warn("Error while writing response", mlog.Err(err))
|
|
}
|
|
}
|
|
|
|
func validateExpressionAgainstRequester(c *Context, w http.ResponseWriter, r *http.Request) {
|
|
var request struct {
|
|
Expression string `json:"expression"`
|
|
ChannelId string `json:"channelId,omitempty"`
|
|
}
|
|
|
|
if jsonErr := json.NewDecoder(r.Body).Decode(&request); jsonErr != nil {
|
|
c.SetInvalidParamWithErr("request", jsonErr)
|
|
return
|
|
}
|
|
|
|
// Get channelId from request body (required for channel-specific permission check)
|
|
channelId := request.ChannelId
|
|
if channelId != "" && !model.IsValidId(channelId) {
|
|
c.SetInvalidParam("channelId")
|
|
return
|
|
}
|
|
|
|
// Check permissions: system admin OR channel-specific permission
|
|
hasSystemPermission := c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionManageSystem)
|
|
if !hasSystemPermission {
|
|
// For channel admins, channelId is required
|
|
if channelId == "" {
|
|
c.SetPermissionError(model.PermissionManageSystem)
|
|
return
|
|
}
|
|
|
|
// SECURE: Check specific channel permission
|
|
hasChannelPermission := c.App.HasPermissionToChannel(c.AppContext, c.AppContext.Session().UserId, channelId, model.PermissionManageChannelAccessRules)
|
|
if !hasChannelPermission {
|
|
c.SetPermissionError(model.PermissionManageChannelAccessRules)
|
|
return
|
|
}
|
|
}
|
|
|
|
// Direct validation against requester
|
|
matches, appErr := c.App.ValidateExpressionAgainstRequester(c.AppContext, request.Expression, c.AppContext.Session().UserId)
|
|
if appErr != nil {
|
|
c.Err = appErr
|
|
return
|
|
}
|
|
|
|
response := struct {
|
|
RequesterMatches bool `json:"requester_matches"`
|
|
}{
|
|
RequesterMatches: matches,
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
if err := json.NewEncoder(w).Encode(response); err != nil {
|
|
c.Logger.Warn("Error while writing response", mlog.Err(err))
|
|
}
|
|
}
|
|
|
|
func searchAccessControlPolicies(c *Context, w http.ResponseWriter, r *http.Request) {
|
|
if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionManageSystem) {
|
|
c.SetPermissionError(model.PermissionManageSystem)
|
|
return
|
|
}
|
|
|
|
var props *model.AccessControlPolicySearch
|
|
err := json.NewDecoder(r.Body).Decode(&props)
|
|
if err != nil || props == nil {
|
|
c.SetInvalidParamWithErr("access_control_policy_search", err)
|
|
return
|
|
}
|
|
|
|
policies, total, appErr := c.App.SearchAccessControlPolicies(c.AppContext, *props)
|
|
if appErr != nil {
|
|
c.Err = appErr
|
|
return
|
|
}
|
|
|
|
result := model.AccessControlPoliciesWithCount{
|
|
Policies: policies,
|
|
Total: total,
|
|
}
|
|
|
|
js, err := json.Marshal(result)
|
|
if err != nil {
|
|
c.Err = model.NewAppError("searchAccessControlPolicies", "api.marshal_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
|
return
|
|
}
|
|
|
|
if _, err := w.Write(js); err != nil {
|
|
c.Logger.Warn("Error while writing response", mlog.Err(err))
|
|
}
|
|
}
|
|
|
|
func updateActiveStatus(c *Context, w http.ResponseWriter, r *http.Request) {
|
|
c.RequirePolicyId()
|
|
if c.Err != nil {
|
|
return
|
|
}
|
|
|
|
policyID := c.Params.PolicyId
|
|
|
|
// Check if user has system admin permission OR channel-specific permission for this policy
|
|
hasManageSystemPermission := c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionManageSystem)
|
|
if !hasManageSystemPermission {
|
|
// For non-system admins, validate policy access permission
|
|
if appErr := c.App.ValidateAccessControlPolicyPermission(c.AppContext, c.AppContext.Session().UserId, policyID); appErr != nil {
|
|
c.SetPermissionError(model.PermissionManageSystem)
|
|
return
|
|
}
|
|
}
|
|
|
|
auditRec := c.MakeAuditRecord(model.AuditEventUpdateActiveStatus, model.AuditStatusFail)
|
|
defer c.LogAuditRec(auditRec)
|
|
model.AddEventParameterToAuditRec(auditRec, "id", policyID)
|
|
|
|
active := r.URL.Query().Get("active")
|
|
if active != "true" && active != "false" {
|
|
c.SetInvalidParam("active")
|
|
return
|
|
}
|
|
activeBool, err := strconv.ParseBool(active)
|
|
if err != nil {
|
|
c.SetInvalidParamWithErr("active", err)
|
|
return
|
|
}
|
|
model.AddEventParameterToAuditRec(auditRec, "active", activeBool)
|
|
|
|
appErr := c.App.UpdateAccessControlPolicyActive(c.AppContext, policyID, activeBool)
|
|
if appErr != nil {
|
|
c.Err = appErr
|
|
return
|
|
}
|
|
|
|
auditRec.Success()
|
|
|
|
// Return success response
|
|
response := map[string]any{
|
|
"status": "OK",
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
if err := json.NewEncoder(w).Encode(response); err != nil {
|
|
c.Logger.Warn("Error while writing response", mlog.Err(err))
|
|
}
|
|
}
|
|
|
|
func setActiveStatus(c *Context, w http.ResponseWriter, r *http.Request) {
|
|
var list model.AccessControlPolicyActiveUpdateRequest
|
|
if jsonErr := json.NewDecoder(r.Body).Decode(&list); jsonErr != nil {
|
|
c.SetInvalidParamWithErr("request", jsonErr)
|
|
return
|
|
}
|
|
|
|
auditRec := c.MakeAuditRecord(model.AuditEventSetActiveStatus, model.AuditStatusFail)
|
|
defer c.LogAuditRec(auditRec)
|
|
model.AddEventParameterAuditableToAuditRec(auditRec, "requested", &list)
|
|
|
|
// Check if user has system admin permission OR channel-specific permission for this policy
|
|
hasManageSystemPermission := c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionManageSystem)
|
|
if !hasManageSystemPermission {
|
|
for _, entry := range list.Entries {
|
|
hasChannelPermission := c.App.HasPermissionToChannel(c.AppContext, c.AppContext.Session().UserId, entry.ID, model.PermissionManageChannelAccessRules)
|
|
if !hasChannelPermission {
|
|
c.SetPermissionError(model.PermissionManageChannelAccessRules)
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
policies, appErr := c.App.UpdateAccessControlPoliciesActive(c.AppContext, list.Entries)
|
|
if appErr != nil {
|
|
c.Err = appErr
|
|
return
|
|
}
|
|
auditRec.Success()
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
if err := json.NewEncoder(w).Encode(policies); err != nil {
|
|
c.Logger.Warn("Error while writing response", mlog.Err(err))
|
|
}
|
|
}
|
|
|
|
func assignAccessPolicy(c *Context, w http.ResponseWriter, r *http.Request) {
|
|
if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionManageSystem) {
|
|
c.SetPermissionError(model.PermissionManageSystem)
|
|
return
|
|
}
|
|
|
|
c.RequirePolicyId()
|
|
if c.Err != nil {
|
|
return
|
|
}
|
|
policyID := c.Params.PolicyId
|
|
|
|
var assignments struct {
|
|
ChannelIds []string `json:"channel_ids"`
|
|
}
|
|
|
|
err := json.NewDecoder(r.Body).Decode(&assignments)
|
|
if err != nil {
|
|
c.SetInvalidParamWithErr("assignments", err)
|
|
return
|
|
}
|
|
|
|
auditRec := c.MakeAuditRecord(model.AuditEventAssignAccessPolicy, model.AuditStatusFail)
|
|
defer c.LogAuditRec(auditRec)
|
|
model.AddEventParameterToAuditRec(auditRec, "id", policyID)
|
|
model.AddEventParameterToAuditRec(auditRec, "channel_ids", assignments.ChannelIds)
|
|
|
|
if len(assignments.ChannelIds) != 0 {
|
|
_, appErr := c.App.AssignAccessControlPolicyToChannels(c.AppContext, policyID, assignments.ChannelIds)
|
|
if appErr != nil {
|
|
c.Err = appErr
|
|
return
|
|
}
|
|
}
|
|
|
|
auditRec.Success()
|
|
}
|
|
|
|
func unassignAccessPolicy(c *Context, w http.ResponseWriter, r *http.Request) {
|
|
if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionManageSystem) {
|
|
c.SetPermissionError(model.PermissionManageSystem)
|
|
return
|
|
}
|
|
|
|
c.RequirePolicyId()
|
|
if c.Err != nil {
|
|
return
|
|
}
|
|
policyID := c.Params.PolicyId
|
|
|
|
var assignments struct {
|
|
ChannelIds []string `json:"channel_ids"`
|
|
}
|
|
|
|
auditRec := c.MakeAuditRecord(model.AuditEventUnassignAccessPolicy, model.AuditStatusFail)
|
|
defer c.LogAuditRec(auditRec)
|
|
model.AddEventParameterToAuditRec(auditRec, "id", policyID)
|
|
model.AddEventParameterToAuditRec(auditRec, "channel_ids", assignments.ChannelIds)
|
|
|
|
err := json.NewDecoder(r.Body).Decode(&assignments)
|
|
if err != nil {
|
|
c.SetInvalidParamWithErr("assignments", err)
|
|
return
|
|
}
|
|
|
|
if len(assignments.ChannelIds) != 0 {
|
|
appErr := c.App.UnassignPoliciesFromChannels(c.AppContext, policyID, assignments.ChannelIds)
|
|
if appErr != nil {
|
|
c.Err = appErr
|
|
return
|
|
}
|
|
}
|
|
|
|
auditRec.Success()
|
|
}
|
|
|
|
func getChannelsForAccessControlPolicy(c *Context, w http.ResponseWriter, r *http.Request) {
|
|
if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionManageSystem) {
|
|
c.SetPermissionError(model.PermissionManageSystem)
|
|
return
|
|
}
|
|
|
|
c.RequirePolicyId()
|
|
if c.Err != nil {
|
|
return
|
|
}
|
|
policyID := c.Params.PolicyId
|
|
|
|
afterID := r.URL.Query().Get("after")
|
|
if afterID != "" && !model.IsValidId(afterID) {
|
|
c.SetInvalidParam("after")
|
|
return
|
|
}
|
|
|
|
limitStr := r.URL.Query().Get("limit")
|
|
limit, err := strconv.Atoi(limitStr)
|
|
if err != nil {
|
|
c.Err = model.NewAppError("getChannelsForAccessControlPolicy", "api.access_control_policy.get_channels.limit.app_error", nil, "", http.StatusBadRequest).Wrap(err)
|
|
return
|
|
}
|
|
|
|
channels, total, appErr := c.App.GetChannelsForPolicy(c.AppContext, policyID, model.AccessControlPolicyCursor{
|
|
ID: afterID,
|
|
}, limit)
|
|
if appErr != nil {
|
|
c.Err = appErr
|
|
return
|
|
}
|
|
|
|
data := model.ChannelsWithCount{Channels: channels, TotalCount: total}
|
|
|
|
js, err := json.Marshal(data)
|
|
if err != nil {
|
|
c.Err = model.NewAppError("getChannelsForAccessControlPolicy", "api.marshal_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
|
return
|
|
}
|
|
|
|
if _, err := w.Write(js); err != nil {
|
|
c.Logger.Warn("Error while writing response", mlog.Err(err))
|
|
}
|
|
}
|
|
|
|
func searchChannelsForAccessControlPolicy(c *Context, w http.ResponseWriter, r *http.Request) {
|
|
if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionManageSystem) {
|
|
c.SetPermissionError(model.PermissionManageSystem)
|
|
return
|
|
}
|
|
|
|
c.RequirePolicyId()
|
|
if c.Err != nil {
|
|
return
|
|
}
|
|
|
|
var props *model.ChannelSearch
|
|
err := json.NewDecoder(r.Body).Decode(&props)
|
|
if err != nil || props == nil {
|
|
c.SetInvalidParamWithErr("channel_search", err)
|
|
return
|
|
}
|
|
|
|
policyID := c.Params.PolicyId
|
|
|
|
c.RequirePolicyId()
|
|
|
|
opts := model.ChannelSearchOpts{
|
|
Deleted: props.Deleted,
|
|
IncludeDeleted: props.IncludeDeleted,
|
|
Private: true,
|
|
ExcludeGroupConstrained: true,
|
|
TeamIds: props.TeamIds,
|
|
ParentAccessControlPolicyId: policyID,
|
|
}
|
|
|
|
channels, total, appErr := c.App.SearchAllChannels(c.AppContext, props.Term, opts)
|
|
if appErr != nil {
|
|
c.Err = appErr
|
|
return
|
|
}
|
|
|
|
data := model.ChannelsWithCount{Channels: channels, TotalCount: total}
|
|
|
|
channelsJSON, jsonErr := json.Marshal(data)
|
|
if jsonErr != nil {
|
|
c.Err = model.NewAppError("searchChannelsInPolicy", "api.marshal_error", nil, "", http.StatusInternalServerError).Wrap(jsonErr)
|
|
return
|
|
}
|
|
|
|
if _, err := w.Write(channelsJSON); err != nil {
|
|
c.Logger.Warn("Error while writing response", mlog.Err(err))
|
|
}
|
|
}
|
|
|
|
func getFieldsAutocomplete(c *Context, w http.ResponseWriter, r *http.Request) {
|
|
// Get channelId from query parameters (required for channel-specific permission check)
|
|
channelId := r.URL.Query().Get("channelId")
|
|
if channelId != "" && !model.IsValidId(channelId) {
|
|
c.SetInvalidParam("channelId")
|
|
return
|
|
}
|
|
|
|
// Check permissions: system admin OR channel-specific permission
|
|
hasSystemPermission := c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionManageSystem)
|
|
if !hasSystemPermission {
|
|
// For channel admins, channelId is required
|
|
if channelId == "" {
|
|
c.SetPermissionError(model.PermissionManageSystem)
|
|
return
|
|
}
|
|
|
|
// SECURE: Check specific channel permission
|
|
hasChannelPermission := c.App.HasPermissionToChannel(c.AppContext, c.AppContext.Session().UserId, channelId, model.PermissionManageChannelAccessRules)
|
|
if !hasChannelPermission {
|
|
c.SetPermissionError(model.PermissionManageChannelAccessRules)
|
|
return
|
|
}
|
|
}
|
|
|
|
after := r.URL.Query().Get("after")
|
|
if after != "" && !model.IsValidId(after) {
|
|
c.SetInvalidParam("after")
|
|
return
|
|
} else if after == "" {
|
|
after = strings.Repeat("0", 26)
|
|
}
|
|
|
|
limitStr := r.URL.Query().Get("limit")
|
|
limit, err := strconv.Atoi(limitStr)
|
|
if err != nil {
|
|
c.Err = model.NewAppError("getFieldsAutocomplete", "api.access_control_policy.get_fields.limit.app_error", nil, "", http.StatusBadRequest).Wrap(err)
|
|
return
|
|
}
|
|
if limit <= 0 || limit > 100 {
|
|
c.Err = model.NewAppError("getFieldsAutocomplete", "api.access_control_policy.get_fields.limit.app_error", nil, "", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
var ac []*model.PropertyField
|
|
var appErr *model.AppError
|
|
|
|
ac, appErr = c.App.GetAccessControlFieldsAutocomplete(c.AppContext, after, limit)
|
|
|
|
if appErr != nil {
|
|
c.Err = appErr
|
|
return
|
|
}
|
|
|
|
js, err := json.Marshal(ac)
|
|
if err != nil {
|
|
c.Err = model.NewAppError("getExpressionAutocomplete", "api.marshal_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
|
return
|
|
}
|
|
|
|
if _, err := w.Write(js); err != nil {
|
|
c.Logger.Warn("Error while writing response", mlog.Err(err))
|
|
}
|
|
}
|
|
|
|
func convertToVisualAST(c *Context, w http.ResponseWriter, r *http.Request) {
|
|
var cel struct {
|
|
Expression string `json:"expression"`
|
|
ChannelId string `json:"channelId,omitempty"`
|
|
}
|
|
if jsonErr := json.NewDecoder(r.Body).Decode(&cel); jsonErr != nil {
|
|
c.SetInvalidParamWithErr("user", jsonErr)
|
|
return
|
|
}
|
|
|
|
// Get channelId from request body (required for channel-specific permission check)
|
|
channelId := cel.ChannelId
|
|
if channelId != "" && !model.IsValidId(channelId) {
|
|
c.SetInvalidParam("channelId")
|
|
return
|
|
}
|
|
|
|
// Check permissions: system admin OR channel-specific permission
|
|
hasSystemPermission := c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionManageSystem)
|
|
if !hasSystemPermission {
|
|
// For channel admins, channelId is required
|
|
if channelId == "" {
|
|
c.SetPermissionError(model.PermissionManageSystem)
|
|
return
|
|
}
|
|
|
|
// SECURE: Check specific channel permission
|
|
hasChannelPermission := c.App.HasPermissionToChannel(c.AppContext, c.AppContext.Session().UserId, channelId, model.PermissionManageChannelAccessRules)
|
|
if !hasChannelPermission {
|
|
c.SetPermissionError(model.PermissionManageChannelAccessRules)
|
|
return
|
|
}
|
|
}
|
|
visualAST, appErr := c.App.ExpressionToVisualAST(c.AppContext, cel.Expression)
|
|
if appErr != nil {
|
|
c.Err = appErr
|
|
return
|
|
}
|
|
|
|
b, err := json.Marshal(visualAST)
|
|
if err != nil {
|
|
c.Err = model.NewAppError("convertToVisualAST", "api.marshal_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
|
return
|
|
}
|
|
if _, err := w.Write(b); err != nil {
|
|
c.Logger.Warn("Error while writing response", mlog.Err(err))
|
|
}
|
|
}
|