forgejo/docs/unsure-where-to-put/adr-map-federated-person.md

367 lines
9.2 KiB
Markdown
Raw Normal View History

2024-01-15 08:12:40 +01:00
# Activity for federated star action
2024-01-16 08:28:12 +01:00
- [Activity for federated star action](#activity-for-federated-star-action)
- [Status](#status)
- [Context](#context)
- [Decision](#decision)
- [Choices](#choices)
- [1. Map to User only](#1-map-to-user-only)
- [2. Map to User-2-ExternalLoginUser](#2-map-to-user-2-externalloginuser)
- [3. Map to User-2-FederatedUser](#3-map-to-user-2-federateduser)
- [3. Map to new FederatedPerson and introduce a common User interface](#3-map-to-new-federatedperson-and-introduce-a-common-user-interface)
2024-01-15 08:12:40 +01:00
## Status
Still in discussion
## Context
2024-01-18 19:20:15 +01:00
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.
2024-01-15 08:12:40 +01:00
## Decision
tbd
## Choices
2024-01-16 08:28:12 +01:00
### 1. Map to User only
Triggering forgejo actions stays as is, no new model & persistence is introduced.
2024-01-15 08:12:40 +01:00
2024-01-18 19:20:15 +01:00
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:
2024-01-15 08:12:40 +01:00
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
```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 {
2024-01-16 08:28:12 +01:00
<<Aggragate Root>>
2024-01-15 08:12:40 +01:00
ID int64
LowerName string
Name string
Email string
Passwd string
LoginName string
Type UserType
IsActive bool
IsAdmin bool
}
}
PersonID -- User: mapped by AsLoginName() == LoginName
```
2024-01-16 08:28:12 +01:00
### 2. Map to User-2-ExternalLoginUser
2024-01-15 08:12:40 +01:00
2024-01-16 08:28:12 +01:00
Would improve the ability to map to the federation source.
2024-01-15 08:12:40 +01:00
But login Propagation stuff is not going to be used and will maybe be harmful.
2024-01-16 08:28:12 +01:00
1. We map PersonId.AsWebfinger() (e.g. 13@some.instan.ce) to ExternalLoginUser.ExternalID. LoginSourceID may be left Empty.
2. We accept only URIs as Actor Items
3. We can lookup for federated users without fetching the Person every time.
4. 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
5. Created ExternalLoginUser is limited
1. Login via fediverse is not intended and will not work
2024-01-15 08:12:40 +01:00
```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 {
2024-01-16 08:28:12 +01:00
AsWebfinger() string // "ID@Host"
2024-01-15 08:12:40 +01:00
}
}
ActorID <|-- PersonID
ForgeLike *-- PersonID: ActorID
namespace user {
class User {
2024-01-16 08:28:12 +01:00
<<Aggregate Root>>
2024-01-15 08:12:40 +01:00
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 {
2024-01-16 08:28:12 +01:00
<<Aggregate Root>>
2024-01-15 08:12:40 +01:00
ID int64
Type Type
Name string
IsActive bool
IsSyncEnabled bool
}
}
User *-- ExternalLoginUser: ExternalLoginUser.UserID
User -- Source
ExternalLoginUser -- Source
```
2024-01-16 08:28:12 +01:00
### 3. Map to User-2-FederatedUser
2024-01-15 08:12:40 +01:00
2024-01-16 08:28:12 +01:00
Would improve the ability to map to the federation source. But we will have a additional model & table for FederatedUser
1. We map PersonId.asWbfinger() to FederatedPerson.ExternalID (e.g. 13@some.instan.ce).
2. We accept only URIs as Actor Items
3. We can lookup for federated users without fetching the Person every time.
4. 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
5. Created ExternalLoginUser is limited
1. Login via fediverse is not intended and will not work
2024-01-15 08:12:40 +01:00
```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 {
2024-01-16 08:28:12 +01:00
<<Aggregate Root>>
2024-01-15 08:12:40 +01:00
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
2024-01-16 08:28:12 +01:00
ExternalID string
FederationHost int64
2024-01-15 08:12:40 +01:00
}
2024-01-16 08:28:12 +01:00
}
User *-- FederatedUser: FederatedUser.UserID
PersonID -- FederatedUser : mapped by PersonID.asWebfinger() == FederatedUser.externalID
namespace forgefed {
class FederationHost {
<<Aggregate Root>>
2024-01-15 08:12:40 +01:00
ID int64
HostFqdn string
2024-01-16 08:28:12 +01:00
}
class NodeInfo {
Source string
2024-01-15 08:12:40 +01:00
}
}
2024-01-16 08:28:12 +01:00
FederationHost *-- NodeInfo
FederatedUser -- FederationHost
2024-01-15 08:12:40 +01:00
2024-01-16 08:28:12 +01:00
2024-01-15 08:12:40 +01:00
```
2024-01-16 08:28:12 +01:00
### 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.
1. We map PersonId.asWbfinger() to FederatedPerson.ExternalID (e.g. 13@some.instan.ce).
2. We accept only URIs as Actor Items
3. We can lookup for federated persons without fetching the Person every time.
```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 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
```