Documentation
¶
Overview ¶
Package link is what a person linking their GitHub account leaves behind, shared by the service that writes a link and the controller that keeps checking it.
A link is a GitHub account's id beside the work addresses GitHub verified for it, which the directory knew when the person linked. It is the only way a GitHub account is matched to a person on an organisation that GitHub does not disclose members' addresses to — every organisation not on its Enterprise Cloud plan.
It carries the person's token pair, so the controller can ask GitHub again, every pass, whether those addresses are still verified on the account. That is why a link lives in a Secret. The pair can read one thing — the account's own addresses — because it is a token for the link App, which asks for nothing else.
Index ¶
- Constants
- Variables
- func Encode(l Link) ([]byte, error)
- func EncodeApp(a App) (string, error)
- func EncodeAppCredential(c AppCredential) ([]byte, error)
- func IDOfKey(key string) (int64, bool)
- func Key(id int64) string
- func Normalise(emails []string) []string
- func SecretName(release string) string
- type App
- type AppCredential
- type AppStore
- type Link
- type Links
- type Source
- type State
Constants ¶
const AppKey = "_link.json"
AppKey is where the link App's record and credential are kept, beside the organisations' in the same two objects. A leading underscore is never an organisation's login, so no organisation's key can be it.
const Version = 1
Version is the document version this build writes and reads.
Variables ¶
var ErrVersion = errors.New("link: unsupported document version")
ErrVersion is a document of a version this build does not read.
Functions ¶
func EncodeAppCredential ¶
func EncodeAppCredential(c AppCredential) ([]byte, error)
EncodeAppCredential writes the App's credential.
func SecretName ¶
SecretName is the object holding every link.
Types ¶
type App ¶
type App struct {
Version int `json:"version"`
Owner string `json:"owner"`
AppID int64 `json:"app_id"`
AppSlug string `json:"app_slug"`
// Purpose is always `link`: EncodeApp sets it. A record written before it
// existed has none and is read as the link App's all the same.
Purpose appid.Purpose `json:"purpose,omitempty"`
// Labels are the App's own, for a reader that selects Apps by them.
Labels map[string]string `json:"labels,omitempty"`
// ClientID is public: it is in every authorize URL.
ClientID string `json:"client_id"`
HTMLURL string `json:"html_url,omitempty"`
ConnectedAt time.Time `json:"connected_at"`
ConnectedBy string `json:"connected_by"`
}
App is the connected link App, as the console shows it.
type AppCredential ¶
type AppCredential struct {
Version int `json:"version"`
AppID int64 `json:"app_id"`
ClientID string `json:"client_id"`
ClientSecret string `json:"client_secret"`
// Record is a copy of the App's record, so that the Secret alone
// restores the link App whose ConfigMap entry is gone.
Record *App `json:"record,omitempty"`
}
AppCredential is what a person's authorization is redeemed and refreshed with.
func DecodeAppCredential ¶
func DecodeAppCredential(raw []byte) (AppCredential, error)
DecodeAppCredential reads the App's credential.
type AppStore ¶ added in v1.76.0
type AppStore interface {
PutLinkApp(ctx context.Context, record App, credential AppCredential) error
LinkApp(ctx context.Context) (App, bool, error)
LinkAppCredential(ctx context.Context) (AppCredential, bool, error)
DeleteLinkApp(ctx context.Context) error
}
AppStore is where the link App is kept: its record, which the console shows, and its credential, which redeems a person's authorization.
type Link ¶
type Link struct {
Version int `json:"version"`
ID int64 `json:"id"`
Login string `json:"login"`
// Source is how the link came to be; empty is a link written before
// sources existed, which were all self-links.
Source Source `json:"source,omitempty"`
// Note says where a link that is not a self-link came from.
Note string `json:"note,omitempty"`
// AppID is the link App the tokens were issued to. A token is only
// ever checked with that App's credentials: checked with another's,
// GitHub would call a perfectly good token unknown.
AppID int64 `json:"app_id"`
// Emails are the addresses the link proves, lowercased and sorted.
Emails []string `json:"emails"`
State State `json:"state"`
// Reason says why a link is lost or unverifiable.
Reason string `json:"reason,omitempty"`
LinkedAt time.Time `json:"linked_at"`
CheckedAt time.Time `json:"checked_at,omitzero"`
ChangedAt time.Time `json:"changed_at,omitzero"`
// Revision counts writes. A writer that read a link at one revision
// replaces it only if it is still at that revision, so a person
// linking again is never overwritten by a check that started before.
Revision int64 `json:"revision"`
AccessToken string `json:"access_token,omitempty"`
AccessExpires time.Time `json:"access_expires,omitzero"`
RefreshToken string `json:"refresh_token,omitempty"`
// RefreshExpires is when the refresh token stops working unused.
RefreshExpires time.Time `json:"refresh_expires,omitzero"`
// RefreshingSince is written BEFORE the pair is refreshed and cleared
// after the new pair is kept. GitHub kills the old pair the moment it
// issues a new one, so a crash in between leaves a link whose tokens
// are all dead — which, without this, would read exactly like a
// revoked authorization and remove the person.
RefreshingSince time.Time `json:"refreshing_since,omitzero"`
}
Link is one GitHub account's link.
func Adopt ¶
Adopt adds links that were not made by the person — matched from a profile, or imported — without ever displacing one that was. A candidate is skipped when its account already has a link that counts, and loses any address another account's link already proves. It returns the links to write and, by account id, why each skipped candidate was.
func Claim ¶
Claim is a person linking an account: the new link, and every other link that proved one of its addresses, narrowed to what it still proves.
An address proves one account. When a person links a second account with the same verified address, the address moves to the one they just linked: they are the only person who can have verified it on both, and the account they linked last is the one they mean. An account left with nothing to prove is lost, and leaves the organisation.
func Invalidate ¶
Invalidate makes every self-linked account unverifiable, forgetting its tokens: what disconnecting the link App does, because nothing can check those tokens any more.
func (Link) Checked ¶
Checked reports whether the controller re-checks the link on GitHub: a self-link, which holds the person's tokens. The other sources hold none.
type Links ¶ added in v1.76.0
type Links interface {
List(ctx context.Context) ([]Link, error)
Claim(ctx context.Context, claimed Link, now time.Time) ([]Link, error)
Adopt(ctx context.Context, candidates []Link) ([]Link, map[int64]string, error)
Invalidate(ctx context.Context, reason string, now time.Time) (int, error)
}
Links is where people's links are kept.
type Source ¶
type Source string
Source is how a link came to be.
const ( // SourceSelf: the person authorized the link App. GitHub verified the // addresses, and they are checked again every pass. SourceSelf Source = "self" // SourceProfile: the account publishes the work address on its profile, // which GitHub allows only for a verified address. Proven when matched; // hiding the address later removes nobody. SourceProfile Source = "profile" // SourceImported: an approved pairing from the records github-roster // 0.x kept. Declared, not re-checked on GitHub. SourceImported Source = "imported" )
The sources, strongest first.
type State ¶
type State string
State is where a link stands.
const ( // StateLinked: GitHub still verifies at least one of the addresses. StateLinked State = "linked" // StateLost: GitHub said, in so many words, that the proof is gone — // every linked address is gone or unverified, or the person revoked the // authorization. The account leaves the organisation. StateLost State = "lost" // StateUnverifiable: the link cannot be checked any more, and GitHub // did not say it is gone — the token pair was lost in a refresh, or the // link App it was made with was disconnected. Nothing is removed on // its account and nothing is added; the person links again. StateUnverifiable State = "unverifiable" )
The states.