Files
libredesk/internal/oidc/oidc.go
2025-08-30 18:46:37 +05:30

148 lines
4.5 KiB
Go

package oidc
import (
"database/sql"
"embed"
"fmt"
"strings"
"github.com/abhinavxd/libredesk/internal/dbutil"
"github.com/abhinavxd/libredesk/internal/envelope"
"github.com/abhinavxd/libredesk/internal/oidc/models"
"github.com/abhinavxd/libredesk/internal/stringutil"
"github.com/jmoiron/sqlx"
"github.com/knadh/go-i18n"
"github.com/zerodha/logf"
)
var (
//go:embed queries.sql
efs embed.FS
redirectURL = "/api/v1/oidc/%d/finish"
)
// Manager handles oidc-related operations.
type Manager struct {
q queries
lo *logf.Logger
i18n *i18n.I18n
setting settingsStore
}
// Opts contains options for initializing the Manager.
type Opts struct {
DB *sqlx.DB
Lo *logf.Logger
I18n *i18n.I18n
}
// queries contains prepared SQL queries.
type queries struct {
GetAllOIDC *sqlx.Stmt `query:"get-all-oidc"`
GetOIDC *sqlx.Stmt `query:"get-oidc"`
InsertOIDC *sqlx.Stmt `query:"insert-oidc"`
UpdateOIDC *sqlx.Stmt `query:"update-oidc"`
DeleteOIDC *sqlx.Stmt `query:"delete-oidc"`
}
type settingsStore interface {
GetAppRootURL() (string, error)
}
// New creates and returns a new instance of the oidc Manager.
func New(opts Opts, setting settingsStore) (*Manager, error) {
var q queries
if err := dbutil.ScanSQLFile("queries.sql", &q, opts.DB, efs); err != nil {
return nil, err
}
return &Manager{
q: q,
lo: opts.Lo,
i18n: opts.I18n,
setting: setting,
}, nil
}
// Get returns an oidc by id.
func (o *Manager) Get(id int, includeSecret bool) (models.OIDC, error) {
var oidc models.OIDC
if err := o.q.GetOIDC.Get(&oidc, id); err != nil {
if err == sql.ErrNoRows {
return oidc, envelope.NewError(envelope.NotFoundError, o.i18n.Ts("globals.messages.notFound", "name", "{globals.terms.oidcProvider}"), nil)
}
o.lo.Error("error fetching oidc", "error", err)
return oidc, envelope.NewError(envelope.GeneralError, o.i18n.Ts("globals.messages.errorFetching", "name", "{globals.terms.oidcProvider}"), nil)
}
// Set logo and redirect URL.
oidc.SetProviderLogo()
rootURL, err := o.setting.GetAppRootURL()
if err != nil {
return models.OIDC{}, err
}
oidc.RedirectURI = fmt.Sprintf(rootURL+redirectURL, oidc.ID)
// If secret is not to be included, replace it with dummy characters.
if oidc.ClientSecret != "" && !includeSecret {
oidc.ClientSecret = strings.Repeat(stringutil.PasswordDummy, 10)
}
return oidc, nil
}
// GetAll retrieves all oidc.
func (o *Manager) GetAll() ([]models.OIDC, error) {
var oidc = make([]models.OIDC, 0)
if err := o.q.GetAllOIDC.Select(&oidc); err != nil {
o.lo.Error("error fetching oidc", "error", err)
return oidc, envelope.NewError(envelope.GeneralError, o.i18n.Ts("globals.messages.errorFetching", "name", "{globals.terms.oidcProvider}"), nil)
}
// Get root URL of the app.
rootURL, err := o.setting.GetAppRootURL()
if err != nil {
return nil, err
}
// Set logo and redirect URL.
for i := range oidc {
oidc[i].RedirectURI = fmt.Sprintf(rootURL+redirectURL, oidc[i].ID)
oidc[i].SetProviderLogo()
}
return oidc, nil
}
// Create adds a new oidc.
func (o *Manager) Create(oidc models.OIDC) (models.OIDC, error) {
var createdOIDC models.OIDC
if err := o.q.InsertOIDC.Get(&createdOIDC, oidc.Name, oidc.Provider, oidc.ProviderURL, oidc.ClientID, oidc.ClientSecret); err != nil {
o.lo.Error("error inserting oidc", "error", err)
return models.OIDC{}, envelope.NewError(envelope.GeneralError, o.i18n.Ts("globals.messages.errorCreating", "name", "{globals.terms.oidcProvider}"), nil)
}
return createdOIDC, nil
}
// Update updates a oidc by id.
func (o *Manager) Update(id int, oidc models.OIDC) (models.OIDC, error) {
current, err := o.Get(id, true)
if err != nil {
return models.OIDC{}, err
}
if oidc.ClientSecret == "" {
oidc.ClientSecret = current.ClientSecret
}
var updatedOIDC models.OIDC
if err := o.q.UpdateOIDC.Get(&updatedOIDC, id, oidc.Name, oidc.Provider, oidc.ProviderURL, oidc.ClientID, oidc.ClientSecret, oidc.Enabled); err != nil {
o.lo.Error("error updating oidc", "error", err)
return models.OIDC{}, envelope.NewError(envelope.GeneralError, o.i18n.Ts("globals.messages.errorUpdating", "name", "{globals.terms.oidcProvider}"), nil)
}
return updatedOIDC, nil
}
// Delete deletes a oidc by its id.
func (o *Manager) Delete(id int) error {
if _, err := o.q.DeleteOIDC.Exec(id); err != nil {
o.lo.Error("error deleting oidc", "error", err)
return envelope.NewError(envelope.GeneralError, o.i18n.Ts("globals.messages.errorDeleting", "name", "{globals.terms.oidcProvider}"), nil)
}
return nil
}