Rename root, api and sdk module paths (imports, go.mod, go_package, ldflags, build scripts, buf configs) and regenerate protobuf/gateway code with the project's pinned toolchain. Wire identifiers unchanged. api gets a local replace for sdk; root/api/sdk require v0.1.0. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
156 lines
4.8 KiB
Go
156 lines
4.8 KiB
Go
// Copyright IBM Corp. 2020, 2026
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package oidc
|
|
|
|
import (
|
|
"context"
|
|
"net/url"
|
|
|
|
"repo.anhonesthost.net/CyberCoveLLC/threshold/internal/auth/oidc/store"
|
|
"repo.anhonesthost.net/CyberCoveLLC/threshold/internal/db/timestamp"
|
|
"repo.anhonesthost.net/CyberCoveLLC/threshold/internal/errors"
|
|
"repo.anhonesthost.net/CyberCoveLLC/threshold/internal/oplog"
|
|
"repo.anhonesthost.net/CyberCoveLLC/threshold/internal/types/resource"
|
|
"google.golang.org/protobuf/proto"
|
|
)
|
|
|
|
// defaultAccountTableName defines the default table name for an Account
|
|
const defaultAccountTableName = "auth_oidc_account"
|
|
|
|
// Account contains an OIDC auth account. It is assigned to an OIDC AuthMethod
|
|
// and updates/deletes to that AuthMethod are cascaded to its Accounts.
|
|
type Account struct {
|
|
*store.Account
|
|
tableName string
|
|
}
|
|
|
|
// NewAccount creates a new in memory Account assigned to OIDC AuthMethod.
|
|
// WithIssuer, WithFullName, WithEmail, WithName and WithDescription are
|
|
// the only valid options. All other options are ignored.
|
|
//
|
|
// Subject equals the locally unique and never reassigned identifier within
|
|
// the Issuer for the End-User, which is intended to be consumed by the Client.
|
|
//
|
|
// Issuer equals the Verifiable Identifier for an Issuer. An Issuer
|
|
// Identifier is a case sensitive URL using the https scheme that contains
|
|
// scheme, host, and optionally, port number and path components and no query or
|
|
// fragment components.
|
|
//
|
|
// FullName equals the End-User's full name in displayable form including all name
|
|
// parts, possibly including titles and suffixes, ordered according to the
|
|
// End-User's locale and preferences.
|
|
//
|
|
// Email equals the End-User's preferred e-mail address. Its value MUST conform
|
|
// to the RFC 5322 [RFC5322] addr-spec syntax. The RP MUST NOT rely upon this
|
|
// value being unique
|
|
//
|
|
// See: https://openid.net/specs/openid-connect-core-1_0.html
|
|
func NewAccount(ctx context.Context, authMethodId string, subject string, opt ...Option) (*Account, error) {
|
|
const op = "oidc.NewAccount"
|
|
opts := getOpts(opt...)
|
|
a := &Account{
|
|
Account: &store.Account{
|
|
AuthMethodId: authMethodId,
|
|
Subject: subject,
|
|
Name: opts.withName,
|
|
Description: opts.withDescription,
|
|
FullName: opts.withFullName,
|
|
Email: opts.withEmail,
|
|
},
|
|
}
|
|
if opts.withIssuer != nil {
|
|
a.Issuer = opts.withIssuer.String()
|
|
}
|
|
if err := a.validate(ctx, op); err != nil {
|
|
return nil, err // intentionally not wrapped.
|
|
}
|
|
|
|
return a, nil
|
|
}
|
|
|
|
// validate the Account. On success, it will return nil.
|
|
func (a *Account) validate(ctx context.Context, caller errors.Op) error {
|
|
if a.AuthMethodId == "" {
|
|
return errors.New(ctx, errors.InvalidParameter, caller, "missing auth method id")
|
|
}
|
|
if a.Subject == "" {
|
|
return errors.New(ctx, errors.InvalidParameter, caller, "missing subject")
|
|
}
|
|
if _, err := url.Parse(a.Issuer); a.Issuer != "" && err != nil {
|
|
return errors.New(ctx, errors.InvalidParameter, caller, "not a valid issuer", errors.WithWrap(err))
|
|
}
|
|
if a.Email != "" && len(a.Email) > 320 {
|
|
return errors.New(ctx, errors.InvalidParameter, caller, "email address is too long")
|
|
}
|
|
if a.FullName != "" && len(a.FullName) > 512 {
|
|
return errors.New(ctx, errors.InvalidParameter, caller, "full name is too long")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// AllocAccount makes an empty one in memory
|
|
func AllocAccount() *Account {
|
|
return &Account{
|
|
Account: &store.Account{},
|
|
}
|
|
}
|
|
|
|
// Clone an Account.
|
|
func (a *Account) Clone() *Account {
|
|
cp := proto.Clone(a.Account)
|
|
return &Account{
|
|
Account: cp.(*store.Account),
|
|
}
|
|
}
|
|
|
|
// TableName returns the table name.
|
|
func (a *Account) TableName() string {
|
|
if a.tableName != "" {
|
|
return a.tableName
|
|
}
|
|
return defaultAccountTableName
|
|
}
|
|
|
|
// SetTableName sets the table name.
|
|
func (a *Account) SetTableName(n string) {
|
|
a.tableName = n
|
|
}
|
|
|
|
// GetResourceType returns the resource type of the Account
|
|
func (a *Account) GetResourceType() resource.Type {
|
|
return resource.Account
|
|
}
|
|
|
|
// GetLoginName returns the login name, which will always be empty as this type
|
|
// doesn't currently support login name
|
|
func (a *Account) GetLoginName() string {
|
|
return ""
|
|
}
|
|
|
|
// oplog will create oplog metadata for the Account.
|
|
func (a *Account) oplog(op oplog.OpType, authMethodScopeId string) oplog.Metadata {
|
|
metadata := oplog.Metadata{
|
|
"resource-public-id": []string{a.GetPublicId()},
|
|
"resource-type": []string{"oidc account"},
|
|
"op-type": []string{op.String()},
|
|
}
|
|
if a.AuthMethodId != "" {
|
|
metadata["auth-method-id"] = []string{a.AuthMethodId}
|
|
}
|
|
if authMethodScopeId != "" {
|
|
metadata["scope-id"] = []string{authMethodScopeId}
|
|
}
|
|
return metadata
|
|
}
|
|
|
|
type deletedAccount struct {
|
|
PublicId string `gorm:"primary_key"`
|
|
DeleteTime *timestamp.Timestamp
|
|
}
|
|
|
|
// TableName returns the tablename to override the default gorm table name
|
|
func (s *deletedAccount) TableName() string {
|
|
return "auth_oidc_account_deleted"
|
|
}
|