You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
336 lines
9.9 KiB
336 lines
9.9 KiB
// GoToSocial |
|
// Copyright (C) GoToSocial Authors admin@gotosocial.org |
|
// SPDX-License-Identifier: AGPL-3.0-or-later |
|
// |
|
// This program is free software: you can redistribute it and/or modify |
|
// it under the terms of the GNU Affero General Public License as published by |
|
// the Free Software Foundation, either version 3 of the License, or |
|
// (at your option) any later version. |
|
// |
|
// This program is distributed in the hope that it will be useful, |
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
// GNU Affero General Public License for more details. |
|
// |
|
// You should have received a copy of the GNU Affero General Public License |
|
// along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
|
|
package gtsmodel |
|
|
|
// A policy URI is GoToSocial's internal representation of |
|
// one ActivityPub URI for an Actor or a Collection of Actors, |
|
// specific to the domain of enforcing interaction policies. |
|
// |
|
// A PolicyValue can be stored in the database either as one |
|
// of the Value constants defined below (to save space), OR as |
|
// a full-fledged ActivityPub URI. |
|
// |
|
// A PolicyValue should be translated to the canonical string |
|
// value of the represented URI when federating an item, or |
|
// from the canonical string value of the URI when receiving |
|
// or retrieving an item. |
|
// |
|
// For example, if the PolicyValue `followers` was being |
|
// federated outwards in an interaction policy attached to an |
|
// item created by the actor `https://example.org/users/someone`, |
|
// then it should be translated to their followers URI when sent, |
|
// eg., `https://example.org/users/someone/followers`. |
|
// |
|
// Likewise, if GoToSocial receives an item with an interaction |
|
// policy containing `https://example.org/users/someone/followers`, |
|
// and the item was created by `https://example.org/users/someone`, |
|
// then the followers URI would be converted to `followers` |
|
// for internal storage. |
|
type PolicyValue string |
|
|
|
const ( |
|
// Stand-in for ActivityPub magic public URI, |
|
// which encompasses every possible Actor URI. |
|
PolicyValuePublic PolicyValue = "public" |
|
// Stand-in for the Followers Collection of |
|
// the item owner's Actor. |
|
PolicyValueFollowers PolicyValue = "followers" |
|
// Stand-in for the Following Collection of |
|
// the item owner's Actor. |
|
PolicyValueFollowing PolicyValue = "following" |
|
// Stand-in for the Mutuals Collection of |
|
// the item owner's Actor. |
|
// |
|
// (TODO: Reserved, currently unused). |
|
PolicyValueMutuals PolicyValue = "mutuals" |
|
// Stand-in for Actor URIs tagged in the item. |
|
PolicyValueMentioned PolicyValue = "mentioned" |
|
// Stand-in for the Actor URI of the item owner. |
|
PolicyValueAuthor PolicyValue = "author" |
|
) |
|
|
|
// FeasibleForVisibility returns true if the PolicyValue could feasibly |
|
// be set in a policy for an item with the given visibility, otherwise |
|
// returns false. |
|
// |
|
// For example, PolicyValuePublic could not be set in a policy for an |
|
// item with visibility FollowersOnly, but could be set in a policy |
|
// for an item with visibility Public or Unlocked. |
|
// |
|
// This is not prescriptive, and should be used only to guide policy |
|
// choices. Eg., if a remote instance wants to do something wacky like |
|
// set "anyone can interact with this status" for a Direct visibility |
|
// status, that's their business; our normal visibility filtering will |
|
// prevent users on our instance from actually being able to interact |
|
// unless they can see the status anyway. |
|
func (p PolicyValue) FeasibleForVisibility(v Visibility) bool { |
|
switch p { |
|
|
|
// Mentioned and self Values are |
|
// feasible for any visibility. |
|
case PolicyValueAuthor, |
|
PolicyValueMentioned: |
|
return true |
|
|
|
// Followers/following/mutual Values |
|
// are only feasible for items with |
|
// followers visibility and higher. |
|
case PolicyValueFollowers, |
|
PolicyValueFollowing: |
|
return v == VisibilityFollowersOnly || |
|
v == VisibilityPublic || |
|
v == VisibilityUnlocked |
|
|
|
// Public policy Value only feasible |
|
// for items that are To or CC public. |
|
case PolicyValuePublic: |
|
return v == VisibilityUnlocked || |
|
v == VisibilityPublic |
|
|
|
// Any other combo |
|
// is probably fine. |
|
default: |
|
return true |
|
} |
|
} |
|
|
|
type PolicyValues []PolicyValue |
|
|
|
// PolicyPermission represents the permission |
|
// state for a certain Actor URI and interaction |
|
// type, in relation to a policy. |
|
type PolicyPermission int |
|
|
|
const ( |
|
// Interaction is forbidden for this |
|
// PolicyValue + interaction combination. |
|
PolicyPermissionForbidden PolicyPermission = iota |
|
// Interaction is conditionally permitted |
|
// for this PolicyValue + interaction combo, |
|
// pending approval by the item owner. |
|
PolicyPermissionWithApproval |
|
// Interaction is permitted for this |
|
// PolicyValue + interaction combination. |
|
PolicyPermissionPermitted |
|
) |
|
|
|
// PolicyCheckResult encapsulates the results |
|
// of checking a certain Actor URI + type |
|
// of interaction against an interaction policy. |
|
type PolicyCheckResult struct { |
|
// Permission permitted / |
|
// with approval / forbidden. |
|
Permission PolicyPermission |
|
|
|
// Value that this check matched on. |
|
// Only set if Permission = permitted. |
|
PermittedMatchedOn *PolicyValue |
|
} |
|
|
|
// MatchedOnCollection returns true if this policy check |
|
// result turned up Permitted, and matched based on the |
|
// requester's presence in a followers or following collection. |
|
func (pcr *PolicyCheckResult) MatchedOnCollection() bool { |
|
if !pcr.Permitted() { |
|
// Not permitted at all |
|
// so definitely didn't |
|
// match on collection. |
|
return false |
|
} |
|
|
|
if pcr.PermittedMatchedOn == nil { |
|
return false |
|
} |
|
|
|
return *pcr.PermittedMatchedOn == PolicyValueFollowers || |
|
*pcr.PermittedMatchedOn == PolicyValueFollowing |
|
} |
|
|
|
// Permitted returns true if this policy |
|
// check resulted in Permission = permitted. |
|
func (pcr *PolicyCheckResult) Permitted() bool { |
|
return pcr.Permission == PolicyPermissionPermitted |
|
} |
|
|
|
// Permitted returns true if this policy |
|
// check resulted in Permission = with approval. |
|
func (pcr *PolicyCheckResult) WithApproval() bool { |
|
return pcr.Permission == PolicyPermissionWithApproval |
|
} |
|
|
|
// Permitted returns true if this policy |
|
// check resulted in Permission = forbidden. |
|
func (pcr *PolicyCheckResult) Forbidden() bool { |
|
return pcr.Permission == PolicyPermissionForbidden |
|
} |
|
|
|
// An InteractionPolicy determines which |
|
// interactions will be accepted for an |
|
// item, and according to what rules. |
|
type InteractionPolicy struct { |
|
// Conditions in which a Like |
|
// interaction will be accepted |
|
// for an item with this policy. |
|
CanLike PolicyRules |
|
// Conditions in which a Reply |
|
// interaction will be accepted |
|
// for an item with this policy. |
|
CanReply PolicyRules |
|
// Conditions in which an Announce |
|
// interaction will be accepted |
|
// for an item with this policy. |
|
CanAnnounce PolicyRules |
|
} |
|
|
|
// PolicyRules represents the rules according |
|
// to which a certain interaction is permitted |
|
// to various Actor and Actor Collection URIs. |
|
type PolicyRules struct { |
|
// Always is for PolicyValues who are |
|
// permitted to do an interaction |
|
// without requiring approval. |
|
Always PolicyValues |
|
// WithApproval is for PolicyValues who |
|
// are conditionally permitted to do |
|
// an interaction, pending approval. |
|
WithApproval PolicyValues |
|
} |
|
|
|
// Returns the default interaction policy |
|
// for the given visibility level. |
|
func DefaultInteractionPolicyFor(v Visibility) *InteractionPolicy { |
|
switch v { |
|
case VisibilityPublic: |
|
return DefaultInteractionPolicyPublic() |
|
case VisibilityUnlocked: |
|
return DefaultInteractionPolicyUnlocked() |
|
case VisibilityFollowersOnly, VisibilityMutualsOnly: |
|
return DefaultInteractionPolicyFollowersOnly() |
|
case VisibilityDirect: |
|
return DefaultInteractionPolicyDirect() |
|
default: |
|
panic("visibility " + v + " not recognized") |
|
} |
|
} |
|
|
|
var defaultPolicyPublic = &InteractionPolicy{ |
|
CanLike: PolicyRules{ |
|
// Anyone can like. |
|
Always: PolicyValues{ |
|
PolicyValuePublic, |
|
}, |
|
WithApproval: make(PolicyValues, 0), |
|
}, |
|
CanReply: PolicyRules{ |
|
// Anyone can reply. |
|
Always: PolicyValues{ |
|
PolicyValuePublic, |
|
}, |
|
WithApproval: make(PolicyValues, 0), |
|
}, |
|
CanAnnounce: PolicyRules{ |
|
// Anyone can announce. |
|
Always: PolicyValues{ |
|
PolicyValuePublic, |
|
}, |
|
WithApproval: make(PolicyValues, 0), |
|
}, |
|
} |
|
|
|
// Returns the default interaction policy |
|
// for a post with visibility of public. |
|
func DefaultInteractionPolicyPublic() *InteractionPolicy { |
|
return defaultPolicyPublic |
|
} |
|
|
|
// Returns the default interaction policy |
|
// for a post with visibility of unlocked. |
|
func DefaultInteractionPolicyUnlocked() *InteractionPolicy { |
|
// Same as public (for now). |
|
return defaultPolicyPublic |
|
} |
|
|
|
var defaultPolicyFollowersOnly = &InteractionPolicy{ |
|
CanLike: PolicyRules{ |
|
// Self, followers and |
|
// mentioned can like. |
|
Always: PolicyValues{ |
|
PolicyValueAuthor, |
|
PolicyValueFollowers, |
|
PolicyValueMentioned, |
|
}, |
|
WithApproval: make(PolicyValues, 0), |
|
}, |
|
CanReply: PolicyRules{ |
|
// Self, followers and |
|
// mentioned can reply. |
|
Always: PolicyValues{ |
|
PolicyValueAuthor, |
|
PolicyValueFollowers, |
|
PolicyValueMentioned, |
|
}, |
|
WithApproval: make(PolicyValues, 0), |
|
}, |
|
CanAnnounce: PolicyRules{ |
|
// Only self can announce. |
|
Always: PolicyValues{ |
|
PolicyValueAuthor, |
|
}, |
|
WithApproval: make(PolicyValues, 0), |
|
}, |
|
} |
|
|
|
// Returns the default interaction policy for |
|
// a post with visibility of followers only. |
|
func DefaultInteractionPolicyFollowersOnly() *InteractionPolicy { |
|
return defaultPolicyFollowersOnly |
|
} |
|
|
|
var defaultPolicyDirect = &InteractionPolicy{ |
|
CanLike: PolicyRules{ |
|
// Mentioned and self |
|
// can always like. |
|
Always: PolicyValues{ |
|
PolicyValueAuthor, |
|
PolicyValueMentioned, |
|
}, |
|
WithApproval: make(PolicyValues, 0), |
|
}, |
|
CanReply: PolicyRules{ |
|
// Mentioned and self |
|
// can always reply. |
|
Always: PolicyValues{ |
|
PolicyValueAuthor, |
|
PolicyValueMentioned, |
|
}, |
|
WithApproval: make(PolicyValues, 0), |
|
}, |
|
CanAnnounce: PolicyRules{ |
|
// Only self can announce. |
|
Always: PolicyValues{ |
|
PolicyValueAuthor, |
|
}, |
|
WithApproval: make(PolicyValues, 0), |
|
}, |
|
} |
|
|
|
// Returns the default interaction policy |
|
// for a post with visibility of direct. |
|
func DefaultInteractionPolicyDirect() *InteractionPolicy { |
|
return defaultPolicyDirect |
|
}
|
|
|