storage: Add OfflineSession object to backend storage.
This commit is contained in:
		@@ -58,6 +58,12 @@ func (c *client) idToName(s string) string {
 | 
			
		||||
	return idToName(s, c.hash)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// offlineTokenName maps two arbitrary IDs, to a single Kubernetes object name.
 | 
			
		||||
// This is used when more than one field is used to uniquely identify the object.
 | 
			
		||||
func (c *client) offlineTokenName(userID string, connID string) string {
 | 
			
		||||
	return offlineTokenName(userID, connID, c.hash)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Kubernetes names must match the regexp '[a-z0-9]([-a-z0-9]*[a-z0-9])?'.
 | 
			
		||||
var encoding = base32.NewEncoding("abcdefghijklmnopqrstuvwxyz234567")
 | 
			
		||||
 | 
			
		||||
@@ -65,6 +71,12 @@ func idToName(s string, h func() hash.Hash) string {
 | 
			
		||||
	return strings.TrimRight(encoding.EncodeToString(h().Sum([]byte(s))), "=")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func offlineTokenName(userID string, connID string, h func() hash.Hash) string {
 | 
			
		||||
	h().Write([]byte(userID))
 | 
			
		||||
	h().Write([]byte(connID))
 | 
			
		||||
	return strings.TrimRight(encoding.EncodeToString(h().Sum(nil)), "=")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *client) urlFor(apiVersion, namespace, resource, name string) string {
 | 
			
		||||
	basePath := "apis/"
 | 
			
		||||
	if apiVersion == "v1" {
 | 
			
		||||
 
 | 
			
		||||
@@ -15,21 +15,23 @@ import (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	kindAuthCode     = "AuthCode"
 | 
			
		||||
	kindAuthRequest  = "AuthRequest"
 | 
			
		||||
	kindClient       = "OAuth2Client"
 | 
			
		||||
	kindRefreshToken = "RefreshToken"
 | 
			
		||||
	kindKeys         = "SigningKey"
 | 
			
		||||
	kindPassword     = "Password"
 | 
			
		||||
	kindAuthCode        = "AuthCode"
 | 
			
		||||
	kindAuthRequest     = "AuthRequest"
 | 
			
		||||
	kindClient          = "OAuth2Client"
 | 
			
		||||
	kindRefreshToken    = "RefreshToken"
 | 
			
		||||
	kindKeys            = "SigningKey"
 | 
			
		||||
	kindPassword        = "Password"
 | 
			
		||||
	kindOfflineSessions = "OfflineSessions"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	resourceAuthCode     = "authcodes"
 | 
			
		||||
	resourceAuthRequest  = "authrequests"
 | 
			
		||||
	resourceClient       = "oauth2clients"
 | 
			
		||||
	resourceRefreshToken = "refreshtokens"
 | 
			
		||||
	resourceKeys         = "signingkeies" // Kubernetes attempts to pluralize.
 | 
			
		||||
	resourcePassword     = "passwords"
 | 
			
		||||
	resourceAuthCode        = "authcodes"
 | 
			
		||||
	resourceAuthRequest     = "authrequests"
 | 
			
		||||
	resourceClient          = "oauth2clients"
 | 
			
		||||
	resourceRefreshToken    = "refreshtokens"
 | 
			
		||||
	resourceKeys            = "signingkeies" // Kubernetes attempts to pluralize.
 | 
			
		||||
	resourcePassword        = "passwords"
 | 
			
		||||
	resourceOfflineSessions = "offlinesessions"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Config values for the Kubernetes storage type.
 | 
			
		||||
@@ -156,6 +158,10 @@ func (cli *client) CreateRefresh(r storage.RefreshToken) error {
 | 
			
		||||
	return cli.post(resourceRefreshToken, cli.fromStorageRefreshToken(r))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (cli *client) CreateOfflineSessions(o storage.OfflineSessions) error {
 | 
			
		||||
	return cli.post(resourceOfflineSessions, cli.fromStorageOfflineSessions(o))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (cli *client) GetAuthRequest(id string) (storage.AuthRequest, error) {
 | 
			
		||||
	var req AuthRequest
 | 
			
		||||
	if err := cli.get(resourceAuthRequest, id, &req); err != nil {
 | 
			
		||||
@@ -235,6 +241,25 @@ func (cli *client) getRefreshToken(id string) (r RefreshToken, err error) {
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (cli *client) GetOfflineSessions(userID string, connID string) (storage.OfflineSessions, error) {
 | 
			
		||||
	o, err := cli.getOfflineSessions(userID, connID)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return storage.OfflineSessions{}, err
 | 
			
		||||
	}
 | 
			
		||||
	return toStorageOfflineSessions(o), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (cli *client) getOfflineSessions(userID string, connID string) (o OfflineSessions, err error) {
 | 
			
		||||
	name := cli.offlineTokenName(userID, connID)
 | 
			
		||||
	if err = cli.get(resourceOfflineSessions, name, &o); err != nil {
 | 
			
		||||
		return OfflineSessions{}, err
 | 
			
		||||
	}
 | 
			
		||||
	if userID != o.UserID || connID != o.ConnID {
 | 
			
		||||
		return OfflineSessions{}, fmt.Errorf("get offline session: wrong session retrieved")
 | 
			
		||||
	}
 | 
			
		||||
	return o, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (cli *client) ListClients() ([]storage.Client, error) {
 | 
			
		||||
	return nil, errors.New("not implemented")
 | 
			
		||||
}
 | 
			
		||||
@@ -292,6 +317,15 @@ func (cli *client) DeletePassword(email string) error {
 | 
			
		||||
	return cli.delete(resourcePassword, p.ObjectMeta.Name)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (cli *client) DeleteOfflineSessions(userID string, connID string) error {
 | 
			
		||||
	// Check for hash collition.
 | 
			
		||||
	o, err := cli.getOfflineSessions(userID, connID)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return cli.delete(resourceOfflineSessions, o.ObjectMeta.Name)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (cli *client) UpdateRefreshToken(id string, updater func(old storage.RefreshToken) (storage.RefreshToken, error)) error {
 | 
			
		||||
	r, err := cli.getRefreshToken(id)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@@ -342,6 +376,22 @@ func (cli *client) UpdatePassword(email string, updater func(old storage.Passwor
 | 
			
		||||
	return cli.put(resourcePassword, p.ObjectMeta.Name, newPassword)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (cli *client) UpdateOfflineSessions(userID string, connID string, updater func(old storage.OfflineSessions) (storage.OfflineSessions, error)) error {
 | 
			
		||||
	o, err := cli.getOfflineSessions(userID, connID)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	updated, err := updater(toStorageOfflineSessions(o))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	newOfflineSessions := cli.fromStorageOfflineSessions(updated)
 | 
			
		||||
	newOfflineSessions.ObjectMeta = o.ObjectMeta
 | 
			
		||||
	return cli.put(resourceOfflineSessions, o.ObjectMeta.Name, newOfflineSessions)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (cli *client) UpdateKeys(updater func(old storage.Keys) (storage.Keys, error)) error {
 | 
			
		||||
	firstUpdate := false
 | 
			
		||||
	var keys Keys
 | 
			
		||||
 
 | 
			
		||||
@@ -66,6 +66,14 @@ var thirdPartyResources = []k8sapi.ThirdPartyResource{
 | 
			
		||||
		Description: "Passwords managed by the OIDC server.",
 | 
			
		||||
		Versions:    []k8sapi.APIVersion{{Name: "v1"}},
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		ObjectMeta: k8sapi.ObjectMeta{
 | 
			
		||||
			Name: "offline-sessions.oidc.coreos.com",
 | 
			
		||||
		},
 | 
			
		||||
		TypeMeta:    tprMeta,
 | 
			
		||||
		Description: "User sessions with an active refresh token.",
 | 
			
		||||
		Versions:    []k8sapi.APIVersion{{Name: "v1"}},
 | 
			
		||||
	},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// There will only ever be a single keys resource. Maintain this by setting a
 | 
			
		||||
@@ -465,3 +473,38 @@ func toStorageKeys(keys Keys) storage.Keys {
 | 
			
		||||
		NextRotation:     keys.NextRotation,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OfflineSessions is a mirrored struct from storage with JSON struct tags and Kubernetes
 | 
			
		||||
// type metadata.
 | 
			
		||||
type OfflineSessions struct {
 | 
			
		||||
	k8sapi.TypeMeta   `json:",inline"`
 | 
			
		||||
	k8sapi.ObjectMeta `json:"metadata,omitempty"`
 | 
			
		||||
 | 
			
		||||
	UserID  string                              `json:"userID,omitempty"`
 | 
			
		||||
	ConnID  string                              `json:"connID,omitempty"`
 | 
			
		||||
	Refresh map[string]*storage.RefreshTokenRef `json:"refresh,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (cli *client) fromStorageOfflineSessions(o storage.OfflineSessions) OfflineSessions {
 | 
			
		||||
	return OfflineSessions{
 | 
			
		||||
		TypeMeta: k8sapi.TypeMeta{
 | 
			
		||||
			Kind:       kindOfflineSessions,
 | 
			
		||||
			APIVersion: cli.apiVersion,
 | 
			
		||||
		},
 | 
			
		||||
		ObjectMeta: k8sapi.ObjectMeta{
 | 
			
		||||
			Name:      cli.offlineTokenName(o.UserID, o.ConnID),
 | 
			
		||||
			Namespace: cli.namespace,
 | 
			
		||||
		},
 | 
			
		||||
		UserID:  o.UserID,
 | 
			
		||||
		ConnID:  o.ConnID,
 | 
			
		||||
		Refresh: o.Refresh,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func toStorageOfflineSessions(o OfflineSessions) storage.OfflineSessions {
 | 
			
		||||
	return storage.OfflineSessions{
 | 
			
		||||
		UserID:  o.UserID,
 | 
			
		||||
		ConnID:  o.ConnID,
 | 
			
		||||
		Refresh: o.Refresh,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user