1
0
Fork 0
mirror of https://codeberg.org/forgejo/forgejo.git synced 2024-12-28 13:49:13 -05:00
forgejo/docs/unsure-where-to-put/adr-map-federated-person.md
2024-02-10 15:18:54 +01:00

383 lines
No EOL
9.7 KiB
Markdown

# Activity for federated star action
- [Activity for federated star action](#activity-for-federated-star-action)
- [Status](#status)
- [Context](#context)
- [Decision](#decision)
- [Choices](#choices)
- [1. Map to plain forgejo User](#1-map-to-plain-forgejo-user)
- [2. Map to User-\&-ExternalLoginUser](#2-map-to-user--externalloginuser)
- [3. Map to User-\&-FederatedUser](#3-map-to-user--federateduser)
- [4. Map to new FederatedPerson and introduce a common User interface](#4-map-to-new-federatedperson-and-introduce-a-common-user-interface)
## Status
Still in discussion
## Context
While implementing federation we have to represent federated persons on a local instance.
A federated person should be able to execute local actions (as if he was a local user), ideally 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",}`. To find out whether this user is available locally without dereference the federated person every time is important for performance & system resilience.
## Decision
tbd
## Choices
### 1. Map to plain forgejo User
1. 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.
2. Created User is limited:
1. non functional email is generated, email notification is false. At the moment we have problems with email whitelists at this point.
2. strong password is generated silently
3. User.Type is UserTypeRemoteUser
4. User is not Admin
5. User is not Active
We can use forgejo code (like star / unstar fkt.) without changes.
No new model & persistence is introduced.
But we use fields against their semantic and see some problems / limitations for mapping arise.
```mermaid
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
PersonID -- Actor: links to
```
### 2. Map to User-&-ExternalLoginUser
1. We map PersonId.AsWebfinger() (e.g. 13@some.instan.ce) to ExternalLoginUser.ExternalID. LoginSourceID may be left Empty.
2. Created User is limited:
1. non functional email is generated, email notification is false.
2. strong password is generated silently
3. User.Type is UserTypeRemoteUser
4. User is not Admin
5. User is not Active
3. Created ExternalLoginUser is limited
1. Login via fediverse is not intended and will not work
We can use forgejo code (like star / unstar fkt.) without changes.
No new model & persistence is introduced, no need for refactorings.
But we use fields against their semantic (User.EMail, User.Password, User.LoginSource, ExternalLoginUser.Login*) and see some problems / limitations for login functionality arise.
Mapping may be more reliable compared to option 1.
```mermaid
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
PersonID -- Actor: links to
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
PersonID -- ExternalLoginUser: mapped by AsLoginName() == ExternalID
```
### 3. Map to User-&-FederatedUser
1. We map PersonId.asWbfinger() to FederatedPerson.ExternalID (e.g. 13@some.instan.ce).
2. Created User is limited:
1. non functional email is generated, email notification is false.
2. strong password is generated silently
3. User.Type is UserTypeRemoteUser
4. User is not Admin
5. User is not Active
We can use forgejo code (like star / unstar fkt.) without changes.
Introduce FederatedUser as new model & persistence.
But we use fields (User.EMail, User.Password) against their semantic, but we probably can handle the problems arising.
We will be able to have a reliable mapping.
```mermaid
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
```
### 4. Map to new FederatedPerson and introduce a common User interface
1. We map PersonId.asWbfinger() to FederatedPerson.ExternalID (e.g. 13@some.instan.ce).
2. We will have no semantic mismatch.
We can use forgejo code (like star / unstar fkt.) after refactorings only. At every place of interaction we have to enhance persistence (e.g. a find may have to query two tables now) & introduce a common User interface.
We introduce new model & persistence.
We will be able to have a reliable mapping.
```mermaid
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
PersonID -- Actor: links to
namespace user {
class CommonUser {
<<Interface>>
}
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
```