9.2 KiB
Activity for federated star action
Status
Still in discussion
Context
While implementing federation we have to represent federated persons to a local instance.
A federated person should be able to execute local actions (as it was a local user) without too many code changes.
For being able to map the federated person reliable, the local representation has to carry a clear mapping to the original federated person.
We get actor information as {"actor": "https://repo.prod.meissa.de/api/v1/activitypub/user-id/1",}
. Find out whether this user is available locally without dereference the federated person is important for performance & system resilience.
Decision
tbd
Choices
1. Map to User only
Triggering forgejo actions stays as is, no new model & persistence is introduced.
- We map PersonId AsLoginName() (e.g. 13-some.instan.ce) to User.LoginName. Due to limitations of User.LoginName validation mapping may be affected by invalid characters.
- Created User is limited:
- non functional email is generated, email notification is false.
- strong password is generated silently
- User.Type is UserTypeRemoteUser
- User is not Admin
- User is not Active
classDiagram
namespace activitypub {
class ForgeLike {
ID ID
Type ActivityVocabularyType // Like
Actor Item
Object Item
}
class Actor {
ID
URL Item
Type ActivityVocabularyType // Person
Name NaturalLanguageValues
PreferredUsername NaturalLanguageValues
Inbox Item
Outbox Item
PublicKey PublicKey
}
class ActorID {
ID string
Source string
Schema string
Path string
Host string
Port string
UnvalidatedInput string
}
class PersonID {
AsLoginName() string // "ID-Host"
}
}
ActorID <|-- PersonID
ForgeLike *-- PersonID: ActorID
namespace forgejo {
class User {
<<Aggragate Root>>
ID int64
LowerName string
Name string
Email string
Passwd string
LoginName string
Type UserType
IsActive bool
IsAdmin bool
}
}
PersonID -- User: mapped by AsLoginName() == LoginName
2. Map to User-2-ExternalLoginUser
Would improve the ability to map to the federation source. But login Propagation stuff is not going to be used and will maybe be harmful.
- We map PersonId.AsWebfinger() (e.g. 13@some.instan.ce) to ExternalLoginUser.ExternalID. LoginSourceID may be left Empty.
- We accept only URIs as Actor Items
- We can lookup for federated users without fetching the Person every time.
- Created User is limited:
- non functional email is generated, email notification is false.
- strong password is generated silently
- User.Type is UserTypeRemoteUser
- User is not Admin
- User is not Active
- Created ExternalLoginUser is limited
- Login via fediverse is not intended and will not work
classDiagram
namespace activitypub {
class ForgeLike {
ID ID
Type ActivityVocabularyType // Like
Actor Item
Object Item
}
class Actor {
ID
URL Item
Type ActivityVocabularyType // Person
Name NaturalLanguageValues
PreferredUsername NaturalLanguageValues
Inbox Item
Outbox Item
PublicKey PublicKey
}
class ActorID {
ID string
Source string
Schema string
Path string
Host string
Port string
UnvalidatedInput string
}
class PersonID {
AsWebfinger() string // "ID@Host"
}
}
ActorID <|-- PersonID
ForgeLike *-- PersonID: ActorID
namespace user {
class User {
<<Aggregate Root>>
ID int64
LoginSource int64
LowerName string
Name string
Email string
Passwd string
LoginName string
Type UserType
IsActive bool
IsAdmin bool
}
class ExternalLoginUser {
ExternalID string
LoginSourceID int64
RawData map[string]any
Provider string
}
}
namespace auth {
class Source {
<<Aggregate Root>>
ID int64
Type Type
Name string
IsActive bool
IsSyncEnabled bool
}
}
User *-- ExternalLoginUser: ExternalLoginUser.UserID
User -- Source
ExternalLoginUser -- Source
3. Map to User-2-FederatedUser
Would improve the ability to map to the federation source. But we will have a additional model & table for FederatedUser
- We map PersonId.asWbfinger() to FederatedPerson.ExternalID (e.g. 13@some.instan.ce).
- We accept only URIs as Actor Items
- We can lookup for federated users without fetching the Person every time.
- Created User is limited:
- non functional email is generated, email notification is false.
- strong password is generated silently
- User.Type is UserTypeRemoteUser
- User is not Admin
- User is not Active
- Created ExternalLoginUser is limited
- Login via fediverse is not intended and will not work
classDiagram
namespace activitypub {
class ForgeLike {
ID ID
Type ActivityVocabularyType // Like
Actor Item
Object Item
}
class Actor {
ID
URL Item
Type ActivityVocabularyType // Person
Name NaturalLanguageValues
PreferredUsername NaturalLanguageValues
Inbox Item
Outbox Item
PublicKey PublicKey
}
class ActorID {
ID string
Source string
Schema string
Path string
Host string
Port string
UnvalidatedInput string
}
class PersonID {
AsLoginName() string // "ID-Host"
AsWebfinger() string // "@ID@Host"
}
}
ActorID <|-- PersonID
ForgeLike *-- PersonID: ActorID
namespace user {
class User {
<<Aggregate Root>>
ID int64
LowerName string
Name string
Email string
Passwd string
LoginName string
Type UserType
IsActive bool
IsAdmin bool
}
class FederatedUser {
ID int64
UserID int64
RawData map[string]any
ExternalID string
FederationHost int64
}
}
User *-- FederatedUser: FederatedUser.UserID
PersonID -- FederatedUser : mapped by PersonID.asWebfinger() == FederatedUser.externalID
namespace forgefed {
class FederationHost {
<<Aggregate Root>>
ID int64
HostFqdn string
}
class NodeInfo {
Source string
}
}
FederationHost *-- NodeInfo
FederatedUser -- FederationHost
3. Map to new FederatedPerson and introduce a common User interface
Cached FederatedPerson is mainly independent to existing User. At every place of interaction we have to enhance persistence & introduce a common User interface.
- We map PersonId.asWbfinger() to FederatedPerson.ExternalID (e.g. 13@some.instan.ce).
- We accept only URIs as Actor Items
- We can lookup for federated persons without fetching the Person every time.
classDiagram
namespace activitypub {
class ForgeLike {
ID ID
Type ActivityVocabularyType // Like
Actor Item
Object Item
}
class Actor {
ID
URL Item
Type ActivityVocabularyType // Person
Name NaturalLanguageValues
PreferredUsername NaturalLanguageValues
Inbox Item
Outbox Item
PublicKey PublicKey
}
class ActorID {
ID string
Source string
Schema string
Path string
Host string
Port string
UnvalidatedInput string
}
class PersonID {
AsLoginName() string // "ID-Host"
AsWebfinger() string // "@ID@Host"
}
}
ActorID <|-- PersonID
ForgeLike *-- PersonID: ActorID
namespace user {
class CommonUser {
}
class User {
}
}
User ..<| CommonUser
namespace forgefed {
class FederatedPerson {
<<Aggregate Root>>
ID int64
UserID int64
RawData map[string]any
ExternalID string
FederationHost int64
}
class FederationHost {
<<Aggregate Root>>
ID int64
HostFqdn string
}
class NodeInfo {
Source string
}
}
PersonID -- FederatedPerson : mapped by PersonID.asWebfinger() == FederatedPerson.externalID
FederationHost *-- NodeInfo
FederatedPerson -- FederationHost
FederatedPerson ..<| CommonUser