This repository has been archived on 2022-08-17. You can view files and clone it, but cannot push or open issues or pull requests.
dex/storage/kubernetes/types.go

851 lines
23 KiB
Go

package kubernetes
import (
"strings"
"time"
jose "gopkg.in/square/go-jose.v2"
"github.com/dexidp/dex/storage"
"github.com/dexidp/dex/storage/kubernetes/k8sapi"
)
const (
apiGroup = "dex.coreos.com"
legacyCRDAPIVersion = "apiextensions.k8s.io/v1beta1"
crdAPIVersion = "apiextensions.k8s.io/v1"
)
// The set of custom resource definitions required by the storage. These are managed by
// the storage so it can migrate itself by creating new resources.
func customResourceDefinitions(apiVersion string) []k8sapi.CustomResourceDefinition {
crdMeta := k8sapi.TypeMeta{
APIVersion: apiVersion,
Kind: "CustomResourceDefinition",
}
var version string
var scope k8sapi.ResourceScope
var versions []k8sapi.CustomResourceDefinitionVersion
switch apiVersion {
case crdAPIVersion:
preserveUnknownFields := true
versions = []k8sapi.CustomResourceDefinitionVersion{
{
Name: "v1",
Served: true,
Storage: true,
Schema: &k8sapi.CustomResourceValidation{
OpenAPIV3Schema: &k8sapi.JSONSchemaProps{
Type: "object",
XPreserveUnknownFields: &preserveUnknownFields,
},
},
},
}
scope = k8sapi.NamespaceScoped
case legacyCRDAPIVersion:
version = "v1"
default:
panic("unknown apiVersion " + apiVersion)
}
return []k8sapi.CustomResourceDefinition{
{
ObjectMeta: k8sapi.ObjectMeta{
Name: "authcodes.dex.coreos.com",
},
TypeMeta: crdMeta,
Spec: k8sapi.CustomResourceDefinitionSpec{
Group: apiGroup,
Version: version,
Versions: versions,
Scope: scope,
Names: k8sapi.CustomResourceDefinitionNames{
Plural: "authcodes",
Singular: "authcode",
Kind: "AuthCode",
},
},
},
{
ObjectMeta: k8sapi.ObjectMeta{
Name: "authrequests.dex.coreos.com",
},
TypeMeta: crdMeta,
Spec: k8sapi.CustomResourceDefinitionSpec{
Group: apiGroup,
Version: version,
Versions: versions,
Scope: scope,
Names: k8sapi.CustomResourceDefinitionNames{
Plural: "authrequests",
Singular: "authrequest",
Kind: "AuthRequest",
},
},
},
{
ObjectMeta: k8sapi.ObjectMeta{
Name: "oauth2clients.dex.coreos.com",
},
TypeMeta: crdMeta,
Spec: k8sapi.CustomResourceDefinitionSpec{
Group: apiGroup,
Version: version,
Versions: versions,
Scope: scope,
Names: k8sapi.CustomResourceDefinitionNames{
Plural: "oauth2clients",
Singular: "oauth2client",
Kind: "OAuth2Client",
},
},
},
{
ObjectMeta: k8sapi.ObjectMeta{
Name: "signingkeies.dex.coreos.com",
},
TypeMeta: crdMeta,
Spec: k8sapi.CustomResourceDefinitionSpec{
Group: apiGroup,
Version: version,
Versions: versions,
Scope: scope,
Names: k8sapi.CustomResourceDefinitionNames{
// `signingkeies` is an artifact from the old TPR pluralization.
// Users don't directly interact with this value, hence leaving it
// as is.
Plural: "signingkeies",
Singular: "signingkey",
Kind: "SigningKey",
},
},
},
{
ObjectMeta: k8sapi.ObjectMeta{
Name: "refreshtokens.dex.coreos.com",
},
TypeMeta: crdMeta,
Spec: k8sapi.CustomResourceDefinitionSpec{
Group: apiGroup,
Version: version,
Versions: versions,
Scope: scope,
Names: k8sapi.CustomResourceDefinitionNames{
Plural: "refreshtokens",
Singular: "refreshtoken",
Kind: "RefreshToken",
},
},
},
{
ObjectMeta: k8sapi.ObjectMeta{
Name: "passwords.dex.coreos.com",
},
TypeMeta: crdMeta,
Spec: k8sapi.CustomResourceDefinitionSpec{
Group: apiGroup,
Version: version,
Versions: versions,
Scope: scope,
Names: k8sapi.CustomResourceDefinitionNames{
Plural: "passwords",
Singular: "password",
Kind: "Password",
},
},
},
{
ObjectMeta: k8sapi.ObjectMeta{
Name: "offlinesessionses.dex.coreos.com",
},
TypeMeta: crdMeta,
Spec: k8sapi.CustomResourceDefinitionSpec{
Group: apiGroup,
Version: version,
Versions: versions,
Scope: scope,
Names: k8sapi.CustomResourceDefinitionNames{
Plural: "offlinesessionses",
Singular: "offlinesessions",
Kind: "OfflineSessions",
},
},
},
{
ObjectMeta: k8sapi.ObjectMeta{
Name: "connectors.dex.coreos.com",
},
TypeMeta: crdMeta,
Spec: k8sapi.CustomResourceDefinitionSpec{
Group: apiGroup,
Version: version,
Versions: versions,
Scope: scope,
Names: k8sapi.CustomResourceDefinitionNames{
Plural: "connectors",
Singular: "connector",
Kind: "Connector",
},
},
},
{
ObjectMeta: k8sapi.ObjectMeta{
Name: "devicerequests.dex.coreos.com",
},
TypeMeta: crdMeta,
Spec: k8sapi.CustomResourceDefinitionSpec{
Group: apiGroup,
Version: version,
Versions: versions,
Scope: scope,
Names: k8sapi.CustomResourceDefinitionNames{
Plural: "devicerequests",
Singular: "devicerequest",
Kind: "DeviceRequest",
},
},
},
{
ObjectMeta: k8sapi.ObjectMeta{
Name: "devicetokens.dex.coreos.com",
},
TypeMeta: crdMeta,
Spec: k8sapi.CustomResourceDefinitionSpec{
Group: apiGroup,
Version: version,
Versions: versions,
Scope: scope,
Names: k8sapi.CustomResourceDefinitionNames{
Plural: "devicetokens",
Singular: "devicetoken",
Kind: "DeviceToken",
},
},
},
}
}
// There will only ever be a single keys resource. Maintain this by setting a
// common name.
const keysName = "openid-connect-keys"
// Client is a mirrored struct from storage with JSON struct tags and
// Kubernetes type metadata.
type Client struct {
// Name is a hash of the ID.
k8sapi.TypeMeta `json:",inline"`
k8sapi.ObjectMeta `json:"metadata,omitempty"`
// ID is immutable, since it's a primary key and should not be changed.
ID string `json:"id,omitempty"`
Secret string `json:"secret,omitempty"`
RedirectURIs []string `json:"redirectURIs,omitempty"`
TrustedPeers []string `json:"trustedPeers,omitempty"`
Public bool `json:"public"`
Name string `json:"name,omitempty"`
LogoURL string `json:"logoURL,omitempty"`
}
// ClientList is a list of Clients.
type ClientList struct {
k8sapi.TypeMeta `json:",inline"`
k8sapi.ListMeta `json:"metadata,omitempty"`
Clients []Client `json:"items"`
}
func (cli *client) fromStorageClient(c storage.Client) Client {
return Client{
TypeMeta: k8sapi.TypeMeta{
Kind: kindClient,
APIVersion: cli.apiVersion,
},
ObjectMeta: k8sapi.ObjectMeta{
Name: cli.idToName(c.ID),
Namespace: cli.namespace,
},
ID: c.ID,
Secret: c.Secret,
RedirectURIs: c.RedirectURIs,
TrustedPeers: c.TrustedPeers,
Public: c.Public,
Name: c.Name,
LogoURL: c.LogoURL,
}
}
func toStorageClient(c Client) storage.Client {
return storage.Client{
ID: c.ID,
Secret: c.Secret,
RedirectURIs: c.RedirectURIs,
TrustedPeers: c.TrustedPeers,
Public: c.Public,
Name: c.Name,
LogoURL: c.LogoURL,
}
}
// Claims is a mirrored struct from storage with JSON struct tags.
type Claims struct {
UserID string `json:"userID"`
Username string `json:"username"`
PreferredUsername string `json:"preferredUsername"`
Email string `json:"email"`
EmailVerified bool `json:"emailVerified"`
Groups []string `json:"groups,omitempty"`
}
func fromStorageClaims(i storage.Claims) Claims {
return Claims{
UserID: i.UserID,
Username: i.Username,
PreferredUsername: i.PreferredUsername,
Email: i.Email,
EmailVerified: i.EmailVerified,
Groups: i.Groups,
}
}
func toStorageClaims(i Claims) storage.Claims {
return storage.Claims{
UserID: i.UserID,
Username: i.Username,
PreferredUsername: i.PreferredUsername,
Email: i.Email,
EmailVerified: i.EmailVerified,
Groups: i.Groups,
}
}
// AuthRequest is a mirrored struct from storage with JSON struct tags and
// Kubernetes type metadata.
type AuthRequest struct {
k8sapi.TypeMeta `json:",inline"`
k8sapi.ObjectMeta `json:"metadata,omitempty"`
ClientID string `json:"clientID"`
ResponseTypes []string `json:"responseTypes,omitempty"`
Scopes []string `json:"scopes,omitempty"`
RedirectURI string `json:"redirectURI"`
Nonce string `json:"nonce,omitempty"`
State string `json:"state,omitempty"`
// The client has indicated that the end user must be shown an approval prompt
// on all requests. The server cannot cache their initial action for subsequent
// attempts.
ForceApprovalPrompt bool `json:"forceApprovalPrompt,omitempty"`
LoggedIn bool `json:"loggedIn"`
// The identity of the end user. Generally nil until the user authenticates
// with a backend.
Claims Claims `json:"claims,omitempty"`
// The connector used to login the user. Set when the user authenticates.
ConnectorID string `json:"connectorID,omitempty"`
ConnectorData []byte `json:"connectorData,omitempty"`
Expiry time.Time `json:"expiry"`
CodeChallenge string `json:"code_challenge,omitempty"`
CodeChallengeMethod string `json:"code_challenge_method,omitempty"`
}
// AuthRequestList is a list of AuthRequests.
type AuthRequestList struct {
k8sapi.TypeMeta `json:",inline"`
k8sapi.ListMeta `json:"metadata,omitempty"`
AuthRequests []AuthRequest `json:"items"`
}
func toStorageAuthRequest(req AuthRequest) storage.AuthRequest {
a := storage.AuthRequest{
ID: req.ObjectMeta.Name,
ClientID: req.ClientID,
ResponseTypes: req.ResponseTypes,
Scopes: req.Scopes,
RedirectURI: req.RedirectURI,
Nonce: req.Nonce,
State: req.State,
ForceApprovalPrompt: req.ForceApprovalPrompt,
LoggedIn: req.LoggedIn,
ConnectorID: req.ConnectorID,
ConnectorData: req.ConnectorData,
Expiry: req.Expiry,
Claims: toStorageClaims(req.Claims),
PKCE: storage.PKCE{
CodeChallenge: req.CodeChallenge,
CodeChallengeMethod: req.CodeChallengeMethod,
},
}
return a
}
func (cli *client) fromStorageAuthRequest(a storage.AuthRequest) AuthRequest {
req := AuthRequest{
TypeMeta: k8sapi.TypeMeta{
Kind: kindAuthRequest,
APIVersion: cli.apiVersion,
},
ObjectMeta: k8sapi.ObjectMeta{
Name: a.ID,
Namespace: cli.namespace,
},
ClientID: a.ClientID,
ResponseTypes: a.ResponseTypes,
Scopes: a.Scopes,
RedirectURI: a.RedirectURI,
Nonce: a.Nonce,
State: a.State,
LoggedIn: a.LoggedIn,
ForceApprovalPrompt: a.ForceApprovalPrompt,
ConnectorID: a.ConnectorID,
ConnectorData: a.ConnectorData,
Expiry: a.Expiry,
Claims: fromStorageClaims(a.Claims),
CodeChallenge: a.PKCE.CodeChallenge,
CodeChallengeMethod: a.PKCE.CodeChallengeMethod,
}
return req
}
// Password is a mirrored struct from the storage with JSON struct tags and
// Kubernetes type metadata.
type Password struct {
k8sapi.TypeMeta `json:",inline"`
k8sapi.ObjectMeta `json:"metadata,omitempty"`
// The Kubernetes name is actually an encoded version of this value.
//
// This field is IMMUTABLE. Do not change.
Email string `json:"email,omitempty"`
Hash []byte `json:"hash,omitempty"`
Username string `json:"username,omitempty"`
UserID string `json:"userID,omitempty"`
}
// PasswordList is a list of Passwords.
type PasswordList struct {
k8sapi.TypeMeta `json:",inline"`
k8sapi.ListMeta `json:"metadata,omitempty"`
Passwords []Password `json:"items"`
}
func (cli *client) fromStoragePassword(p storage.Password) Password {
email := strings.ToLower(p.Email)
return Password{
TypeMeta: k8sapi.TypeMeta{
Kind: kindPassword,
APIVersion: cli.apiVersion,
},
ObjectMeta: k8sapi.ObjectMeta{
Name: cli.idToName(email),
Namespace: cli.namespace,
},
Email: email,
Hash: p.Hash,
Username: p.Username,
UserID: p.UserID,
}
}
func toStoragePassword(p Password) storage.Password {
return storage.Password{
Email: p.Email,
Hash: p.Hash,
Username: p.Username,
UserID: p.UserID,
}
}
// AuthCode is a mirrored struct from storage with JSON struct tags and
// Kubernetes type metadata.
type AuthCode struct {
k8sapi.TypeMeta `json:",inline"`
k8sapi.ObjectMeta `json:"metadata,omitempty"`
ClientID string `json:"clientID"`
Scopes []string `json:"scopes,omitempty"`
RedirectURI string `json:"redirectURI"`
Nonce string `json:"nonce,omitempty"`
State string `json:"state,omitempty"`
Claims Claims `json:"claims,omitempty"`
ConnectorID string `json:"connectorID,omitempty"`
ConnectorData []byte `json:"connectorData,omitempty"`
Expiry time.Time `json:"expiry"`
CodeChallenge string `json:"code_challenge,omitempty"`
CodeChallengeMethod string `json:"code_challenge_method,omitempty"`
}
// AuthCodeList is a list of AuthCodes.
type AuthCodeList struct {
k8sapi.TypeMeta `json:",inline"`
k8sapi.ListMeta `json:"metadata,omitempty"`
AuthCodes []AuthCode `json:"items"`
}
func (cli *client) fromStorageAuthCode(a storage.AuthCode) AuthCode {
return AuthCode{
TypeMeta: k8sapi.TypeMeta{
Kind: kindAuthCode,
APIVersion: cli.apiVersion,
},
ObjectMeta: k8sapi.ObjectMeta{
Name: a.ID,
Namespace: cli.namespace,
},
ClientID: a.ClientID,
RedirectURI: a.RedirectURI,
ConnectorID: a.ConnectorID,
ConnectorData: a.ConnectorData,
Nonce: a.Nonce,
Scopes: a.Scopes,
Claims: fromStorageClaims(a.Claims),
Expiry: a.Expiry,
CodeChallenge: a.PKCE.CodeChallenge,
CodeChallengeMethod: a.PKCE.CodeChallengeMethod,
}
}
func toStorageAuthCode(a AuthCode) storage.AuthCode {
return storage.AuthCode{
ID: a.ObjectMeta.Name,
ClientID: a.ClientID,
RedirectURI: a.RedirectURI,
ConnectorID: a.ConnectorID,
ConnectorData: a.ConnectorData,
Nonce: a.Nonce,
Scopes: a.Scopes,
Claims: toStorageClaims(a.Claims),
Expiry: a.Expiry,
PKCE: storage.PKCE{
CodeChallenge: a.CodeChallenge,
CodeChallengeMethod: a.CodeChallengeMethod,
},
}
}
// RefreshToken is a mirrored struct from storage with JSON struct tags and
// Kubernetes type metadata.
type RefreshToken struct {
k8sapi.TypeMeta `json:",inline"`
k8sapi.ObjectMeta `json:"metadata,omitempty"`
CreatedAt time.Time
LastUsed time.Time
ClientID string `json:"clientID"`
Scopes []string `json:"scopes,omitempty"`
Token string `json:"token,omitempty"`
ObsoleteToken string `json:"obsoleteToken,omitempty"`
Nonce string `json:"nonce,omitempty"`
Claims Claims `json:"claims,omitempty"`
ConnectorID string `json:"connectorID,omitempty"`
ConnectorData []byte `json:"connectorData,omitempty"`
}
// RefreshList is a list of refresh tokens.
type RefreshList struct {
k8sapi.TypeMeta `json:",inline"`
k8sapi.ListMeta `json:"metadata,omitempty"`
RefreshTokens []RefreshToken `json:"items"`
}
func toStorageRefreshToken(r RefreshToken) storage.RefreshToken {
return storage.RefreshToken{
ID: r.ObjectMeta.Name,
Token: r.Token,
ObsoleteToken: r.ObsoleteToken,
CreatedAt: r.CreatedAt,
LastUsed: r.LastUsed,
ClientID: r.ClientID,
ConnectorID: r.ConnectorID,
ConnectorData: r.ConnectorData,
Scopes: r.Scopes,
Nonce: r.Nonce,
Claims: toStorageClaims(r.Claims),
}
}
func (cli *client) fromStorageRefreshToken(r storage.RefreshToken) RefreshToken {
return RefreshToken{
TypeMeta: k8sapi.TypeMeta{
Kind: kindRefreshToken,
APIVersion: cli.apiVersion,
},
ObjectMeta: k8sapi.ObjectMeta{
Name: r.ID,
Namespace: cli.namespace,
},
Token: r.Token,
ObsoleteToken: r.ObsoleteToken,
CreatedAt: r.CreatedAt,
LastUsed: r.LastUsed,
ClientID: r.ClientID,
ConnectorID: r.ConnectorID,
ConnectorData: r.ConnectorData,
Scopes: r.Scopes,
Nonce: r.Nonce,
Claims: fromStorageClaims(r.Claims),
}
}
// Keys is a mirrored struct from storage with JSON struct tags and Kubernetes
// type metadata.
type Keys struct {
k8sapi.TypeMeta `json:",inline"`
k8sapi.ObjectMeta `json:"metadata,omitempty"`
// Key for creating and verifying signatures. These may be nil.
SigningKey *jose.JSONWebKey `json:"signingKey,omitempty"`
SigningKeyPub *jose.JSONWebKey `json:"signingKeyPub,omitempty"`
// Old signing keys which have been rotated but can still be used to validate
// existing signatures.
VerificationKeys []storage.VerificationKey `json:"verificationKeys,omitempty"`
// The next time the signing key will rotate.
//
// For caching purposes, implementations MUST NOT update keys before this time.
NextRotation time.Time `json:"nextRotation"`
}
func (cli *client) fromStorageKeys(keys storage.Keys) Keys {
return Keys{
TypeMeta: k8sapi.TypeMeta{
Kind: kindKeys,
APIVersion: cli.apiVersion,
},
ObjectMeta: k8sapi.ObjectMeta{
Name: keysName,
Namespace: cli.namespace,
},
SigningKey: keys.SigningKey,
SigningKeyPub: keys.SigningKeyPub,
VerificationKeys: keys.VerificationKeys,
NextRotation: keys.NextRotation,
}
}
func toStorageKeys(keys Keys) storage.Keys {
return storage.Keys{
SigningKey: keys.SigningKey,
SigningKeyPub: keys.SigningKeyPub,
VerificationKeys: keys.VerificationKeys,
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"`
ConnectorData []byte `json:"connectorData,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,
ConnectorData: o.ConnectorData,
}
}
func toStorageOfflineSessions(o OfflineSessions) storage.OfflineSessions {
s := storage.OfflineSessions{
UserID: o.UserID,
ConnID: o.ConnID,
Refresh: o.Refresh,
ConnectorData: o.ConnectorData,
}
if s.Refresh == nil {
// Server code assumes this will be non-nil.
s.Refresh = make(map[string]*storage.RefreshTokenRef)
}
return s
}
// Connector is a mirrored struct from storage with JSON struct tags and Kubernetes
// type metadata.
type Connector struct {
k8sapi.TypeMeta `json:",inline"`
k8sapi.ObjectMeta `json:"metadata,omitempty"`
ID string `json:"id,omitempty"`
Type string `json:"type,omitempty"`
Name string `json:"name,omitempty"`
// Config holds connector specific configuration information
Config []byte `json:"config,omitempty"`
}
func (cli *client) fromStorageConnector(c storage.Connector) Connector {
return Connector{
TypeMeta: k8sapi.TypeMeta{
Kind: kindConnector,
APIVersion: cli.apiVersion,
},
ObjectMeta: k8sapi.ObjectMeta{
Name: c.ID,
Namespace: cli.namespace,
},
ID: c.ID,
Type: c.Type,
Name: c.Name,
Config: c.Config,
}
}
func toStorageConnector(c Connector) storage.Connector {
return storage.Connector{
ID: c.ID,
Type: c.Type,
Name: c.Name,
ResourceVersion: c.ObjectMeta.ResourceVersion,
Config: c.Config,
}
}
// ConnectorList is a list of Connectors.
type ConnectorList struct {
k8sapi.TypeMeta `json:",inline"`
k8sapi.ListMeta `json:"metadata,omitempty"`
Connectors []Connector `json:"items"`
}
// DeviceRequest is a mirrored struct from storage with JSON struct tags and
// Kubernetes type metadata.
type DeviceRequest struct {
k8sapi.TypeMeta `json:",inline"`
k8sapi.ObjectMeta `json:"metadata,omitempty"`
DeviceCode string `json:"device_code,omitempty"`
ClientID string `json:"client_id,omitempty"`
ClientSecret string `json:"client_secret,omitempty"`
Scopes []string `json:"scopes,omitempty"`
Expiry time.Time `json:"expiry"`
}
// DeviceRequestList is a list of DeviceRequests.
type DeviceRequestList struct {
k8sapi.TypeMeta `json:",inline"`
k8sapi.ListMeta `json:"metadata,omitempty"`
DeviceRequests []DeviceRequest `json:"items"`
}
func (cli *client) fromStorageDeviceRequest(a storage.DeviceRequest) DeviceRequest {
req := DeviceRequest{
TypeMeta: k8sapi.TypeMeta{
Kind: kindDeviceRequest,
APIVersion: cli.apiVersion,
},
ObjectMeta: k8sapi.ObjectMeta{
Name: strings.ToLower(a.UserCode),
Namespace: cli.namespace,
},
DeviceCode: a.DeviceCode,
ClientID: a.ClientID,
ClientSecret: a.ClientSecret,
Scopes: a.Scopes,
Expiry: a.Expiry,
}
return req
}
func toStorageDeviceRequest(req DeviceRequest) storage.DeviceRequest {
return storage.DeviceRequest{
UserCode: strings.ToUpper(req.ObjectMeta.Name),
DeviceCode: req.DeviceCode,
ClientID: req.ClientID,
ClientSecret: req.ClientSecret,
Scopes: req.Scopes,
Expiry: req.Expiry,
}
}
// DeviceToken is a mirrored struct from storage with JSON struct tags and
// Kubernetes type metadata.
type DeviceToken struct {
k8sapi.TypeMeta `json:",inline"`
k8sapi.ObjectMeta `json:"metadata,omitempty"`
Status string `json:"status,omitempty"`
Token string `json:"token,omitempty"`
Expiry time.Time `json:"expiry"`
LastRequestTime time.Time `json:"last_request"`
PollIntervalSeconds int `json:"poll_interval"`
CodeChallenge string `json:"code_challenge,omitempty"`
CodeChallengeMethod string `json:"code_challenge_method,omitempty"`
}
// DeviceTokenList is a list of DeviceTokens.
type DeviceTokenList struct {
k8sapi.TypeMeta `json:",inline"`
k8sapi.ListMeta `json:"metadata,omitempty"`
DeviceTokens []DeviceToken `json:"items"`
}
func (cli *client) fromStorageDeviceToken(t storage.DeviceToken) DeviceToken {
req := DeviceToken{
TypeMeta: k8sapi.TypeMeta{
Kind: kindDeviceToken,
APIVersion: cli.apiVersion,
},
ObjectMeta: k8sapi.ObjectMeta{
Name: t.DeviceCode,
Namespace: cli.namespace,
},
Status: t.Status,
Token: t.Token,
Expiry: t.Expiry,
LastRequestTime: t.LastRequestTime,
PollIntervalSeconds: t.PollIntervalSeconds,
CodeChallenge: t.PKCE.CodeChallenge,
CodeChallengeMethod: t.PKCE.CodeChallengeMethod,
}
return req
}
func toStorageDeviceToken(t DeviceToken) storage.DeviceToken {
return storage.DeviceToken{
DeviceCode: t.ObjectMeta.Name,
Status: t.Status,
Token: t.Token,
Expiry: t.Expiry,
LastRequestTime: t.LastRequestTime,
PollIntervalSeconds: t.PollIntervalSeconds,
PKCE: storage.PKCE{
CodeChallenge: t.CodeChallenge,
CodeChallengeMethod: t.CodeChallengeMethod,
},
}
}