Compare commits

..

5 Commits

Author SHA1 Message Date
hyung-hwan bf8dd2e441 added config/config.go 2026-06-01 18:53:09 +09:00
hyung-hwan 4cfecbdf76 Added logger/logger.go 2026-06-01 18:51:52 +09:00
hyung-hwan cf45778de0 more refactoring code for reuse 2026-06-01 18:51:05 +09:00
hyung-hwan 930cea4617 refactoring code for reuse 2026-06-01 18:21:13 +09:00
hyung-hwan b80eea5c1f updated db schema doc with diagrams 2026-06-01 16:24:35 +09:00
30 changed files with 2710 additions and 2467 deletions
File diff suppressed because it is too large Load Diff
+18
View File
@@ -0,0 +1,18 @@
package codit
import codit_config "codit/config"
// this public file is to workaround the import-cycle issue.
// codit/config can't import from the root package.
type Config = codit_config.Config
type Duration = codit_config.Duration
func LoadConfig(path string) (Config, error) {
return codit_config.Load(path)
}
func LoadConfigWithEnvPrefix(path string, envPrefix string) (Config, error) {
return codit_config.LoadWithEnvPrefix(path, envPrefix)
}
@@ -45,7 +45,15 @@ type Config struct {
RPMHTTPPrefix string `json:"rpm_http_prefix"`
}
const DefaultEnvPrefix string = "CODIT_"
type Duration time.Duration
func Load(path string) (Config, error) {
return LoadWithEnvPrefix(path, DefaultEnvPrefix)
}
func LoadWithEnvPrefix(path string, envPrefix string) (Config, error) {
var cfg Config
var data []byte
var err error
@@ -55,7 +63,7 @@ func Load(path string) (Config, error) {
DataDir: "./codit-data",
FrontendDir: filepath.Join("..", "frontend", "dist"),
DBDriver: "sqlite",
DBDSN: "file:./codit-data/codit.db?_pragma=foreign_keys(1)",
DBDSN: "file:./codit-data/codit.db",
SessionTTL: Duration(24 * time.Hour),
AuthMode: "db",
LDAPUserFilter: "(uid={username})",
@@ -76,7 +84,7 @@ func Load(path string) (Config, error) {
return cfg, err
}
}
override(&cfg)
override(&cfg, envPrefix)
cfg.AuthMode = strings.ToLower(strings.TrimSpace(cfg.AuthMode))
cfg.TLSServerCertSource = strings.ToLower(strings.TrimSpace(cfg.TLSServerCertSource))
cfg.TLSClientAuth = strings.ToLower(strings.TrimSpace(cfg.TLSClientAuth))
@@ -91,143 +99,147 @@ func Load(path string) (Config, error) {
return cfg, nil
}
func override(cfg *Config) {
func override(cfg *Config, envPrefix string) {
var v string
v = os.Getenv("CODIT_HTTP_ADDRS")
v = getEnv(envPrefix, "HTTP_ADDRS")
if v != "" {
cfg.HTTPAddrs = splitCSV(v)
}
v = os.Getenv("CODIT_HTTPS_ADDRS")
v = getEnv(envPrefix, "HTTPS_ADDRS")
if v != "" {
cfg.HTTPSAddrs = splitCSV(v)
}
v = os.Getenv("CODIT_PUBLIC_BASE_URL")
v = getEnv(envPrefix, "PUBLIC_BASE_URL")
if v != "" {
cfg.PublicBaseURL = v
}
v = os.Getenv("CODIT_DATA_DIR")
v = getEnv(envPrefix, "DATA_DIR")
if v != "" {
cfg.DataDir = v
}
v = os.Getenv("CODIT_FRONTEND_DIR")
v = getEnv(envPrefix, "FRONTEND_DIR")
if v != "" {
cfg.FrontendDir = v
}
v = os.Getenv("CODIT_DB_DRIVER")
v = getEnv(envPrefix, "DB_DRIVER")
if v != "" {
cfg.DBDriver = v
}
v = os.Getenv("CODIT_DB_DSN")
v = getEnv(envPrefix, "DB_DSN")
if v != "" {
cfg.DBDSN = v
}
v = os.Getenv("CODIT_AUTH_MODE")
v = getEnv(envPrefix, "AUTH_MODE")
if v != "" {
cfg.AuthMode = v
}
v = os.Getenv("CODIT_LDAP_URL")
v = getEnv(envPrefix, "LDAP_URL")
if v != "" {
cfg.LDAPURL = v
}
v = os.Getenv("CODIT_LDAP_BIND_DN")
v = getEnv(envPrefix, "LDAP_BIND_DN")
if v != "" {
cfg.LDAPBindDN = v
}
v = os.Getenv("CODIT_LDAP_BIND_PASSWORD")
v = getEnv(envPrefix, "LDAP_BIND_PASSWORD")
if v != "" {
cfg.LDAPBindPassword = v
}
v = os.Getenv("CODIT_LDAP_USER_BASE_DN")
v = getEnv(envPrefix, "LDAP_USER_BASE_DN")
if v != "" {
cfg.LDAPUserBaseDN = v
}
v = os.Getenv("CODIT_LDAP_USER_FILTER")
v = getEnv(envPrefix, "LDAP_USER_FILTER")
if v != "" {
cfg.LDAPUserFilter = v
}
v = os.Getenv("CODIT_LDAP_TLS_INSECURE_SKIP_VERIFY")
v = getEnv(envPrefix, "LDAP_TLS_INSECURE_SKIP_VERIFY")
if v != "" {
cfg.LDAPTLSInsecureSkipVerify = parseEnvBool(v)
}
v = os.Getenv("CODIT_OIDC_CLIENT_ID")
v = getEnv(envPrefix, "OIDC_CLIENT_ID")
if v != "" {
cfg.OIDCClientID = v
}
v = os.Getenv("CODIT_OIDC_CLIENT_SECRET")
v = getEnv(envPrefix, "OIDC_CLIENT_SECRET")
if v != "" {
cfg.OIDCClientSecret = v
}
v = os.Getenv("CODIT_OIDC_AUTHORIZE_URL")
v = getEnv(envPrefix, "OIDC_AUTHORIZE_URL")
if v != "" {
cfg.OIDCAuthorizeURL = v
}
v = os.Getenv("CODIT_OIDC_TOKEN_URL")
v = getEnv(envPrefix, "OIDC_TOKEN_URL")
if v != "" {
cfg.OIDCTokenURL = v
}
v = os.Getenv("CODIT_OIDC_USERINFO_URL")
v = getEnv(envPrefix, "OIDC_USERINFO_URL")
if v != "" {
cfg.OIDCUserInfoURL = v
}
v = os.Getenv("CODIT_OIDC_REDIRECT_URL")
v = getEnv(envPrefix, "OIDC_REDIRECT_URL")
if v != "" {
cfg.OIDCRedirectURL = v
}
v = os.Getenv("CODIT_OIDC_SCOPES")
v = getEnv(envPrefix, "OIDC_SCOPES")
if v != "" {
cfg.OIDCScopes = v
}
v = os.Getenv("CODIT_OIDC_ENABLED")
v = getEnv(envPrefix, "OIDC_ENABLED")
if v != "" {
cfg.OIDCEnabled = parseEnvBool(v)
}
v = os.Getenv("CODIT_OIDC_TLS_INSECURE_SKIP_VERIFY")
v = getEnv(envPrefix, "OIDC_TLS_INSECURE_SKIP_VERIFY")
if v != "" {
cfg.OIDCTLSInsecureSkipVerify = parseEnvBool(v)
}
v = os.Getenv("CODIT_TLS_SERVER_CERT_SOURCE")
v = getEnv(envPrefix, "TLS_SERVER_CERT_SOURCE")
if v != "" {
cfg.TLSServerCertSource = v
}
v = os.Getenv("CODIT_TLS_CERT_FILE")
v = getEnv(envPrefix, "TLS_CERT_FILE")
if v != "" {
cfg.TLSCertFile = v
}
v = os.Getenv("CODIT_TLS_KEY_FILE")
v = getEnv(envPrefix, "TLS_KEY_FILE")
if v != "" {
cfg.TLSKeyFile = v
}
v = os.Getenv("CODIT_TLS_PKI_SERVER_CERT_ID")
v = getEnv(envPrefix, "TLS_PKI_SERVER_CERT_ID")
if v != "" {
cfg.TLSPKIServerCertID = v
}
v = os.Getenv("CODIT_TLS_CLIENT_AUTH")
v = getEnv(envPrefix, "TLS_CLIENT_AUTH")
if v != "" {
cfg.TLSClientAuth = v
}
v = os.Getenv("CODIT_TLS_CLIENT_CA_FILE")
v = getEnv(envPrefix, "TLS_CLIENT_CA_FILE")
if v != "" {
cfg.TLSClientCAFile = v
}
v = os.Getenv("CODIT_TLS_PKI_CLIENT_CA_ID")
v = getEnv(envPrefix, "TLS_PKI_CLIENT_CA_ID")
if v != "" {
cfg.TLSPKIClientCAID = v
}
v = os.Getenv("CODIT_TLS_MIN_VERSION")
v = getEnv(envPrefix, "TLS_MIN_VERSION")
if v != "" {
cfg.TLSMinVersion = v
}
v = os.Getenv("CODIT_GIT_HTTP_PREFIX")
v = getEnv(envPrefix, "GIT_HTTP_PREFIX")
if v != "" {
cfg.GitHTTPPrefix = v
}
v = os.Getenv("CODIT_RPM_HTTP_PREFIX")
v = getEnv(envPrefix, "RPM_HTTP_PREFIX")
if v != "" {
cfg.RPMHTTPPrefix = v
}
}
type Duration time.Duration
func getEnv(prefix string, name string) string {
if prefix == "" { return os.Getenv(name) }
return os.Getenv(prefix + name)
}
func (d Duration) Duration() time.Duration {
return time.Duration(d)
+1 -1
View File
@@ -5,7 +5,7 @@ import "encoding/base64"
import "errors"
import "time"
import "codit/internal/config"
import "codit/config"
import "golang.org/x/crypto/bcrypt"
func HashPassword(password string) (string, error) {
+1 -1
View File
@@ -4,7 +4,7 @@ import "strings"
import "testing"
import "time"
import "codit/internal/config"
import "codit/config"
func TestHashAndComparePassword(t *testing.T) {
var hash string
+1 -1
View File
@@ -7,7 +7,7 @@ import "strings"
import "time"
import "crypto/tls"
import "codit/internal/config"
import "codit/config"
import "github.com/go-ldap/ldap/v3"
type LDAPUser struct {
-73
View File
@@ -1,73 +0,0 @@
package config
import "os"
import "path/filepath"
import "testing"
import "time"
func TestLoadDefaults(t *testing.T) {
var cfg Config
var err error
cfg, err = Load("")
if err != nil {
t.Fatalf("Load() error: %v", err)
}
if cfg.DBDriver == "" || cfg.DBDSN == "" {
t.Fatalf("defaults not populated: driver=%q dsn=%q", cfg.DBDriver, cfg.DBDSN)
}
if cfg.GitHTTPPrefix != "/git" {
t.Fatalf("unexpected git prefix default: %s", cfg.GitHTTPPrefix)
}
if cfg.FrontendDir == "" {
t.Fatalf("frontend_dir default missing")
}
}
func TestLoadFromJSONAndEnvOverride(t *testing.T) {
var dir string
var path string
var data string
var err error
var cfg Config
dir = t.TempDir()
path = filepath.Join(dir, "config.json")
data = `{"db_driver":"sqlite","db_dsn":"file:test.db","auth_mode":"HyBrId","git_http_prefix":"/g"}`
err = os.WriteFile(path, []byte(data), 0o644)
if err != nil {
t.Fatalf("write config file: %v", err)
}
t.Setenv("CODIT_DB_DSN", "file:override.db")
t.Setenv("CODIT_FRONTEND_DIR", "/srv/codit/frontend")
cfg, err = Load(path)
if err != nil {
t.Fatalf("Load() error: %v", err)
}
if cfg.DBDSN != "file:override.db" {
t.Fatalf("env override failed: %s", cfg.DBDSN)
}
if cfg.AuthMode != "hybrid" {
t.Fatalf("auth_mode normalization failed: %s", cfg.AuthMode)
}
if cfg.FrontendDir != "/srv/codit/frontend" {
t.Fatalf("frontend_dir env override failed: %s", cfg.FrontendDir)
}
}
func TestDurationUnmarshalJSON(t *testing.T) {
var d Duration
var err error
err = d.UnmarshalJSON([]byte(`"90m"`))
if err != nil {
t.Fatalf("UnmarshalJSON() string duration error: %v", err)
}
if d.Duration() != 90*time.Minute {
t.Fatalf("unexpected duration: %v", d.Duration())
}
err = d.UnmarshalJSON([]byte(`60000000000`))
if err != nil {
t.Fatalf("UnmarshalJSON() numeric duration error: %v", err)
}
if d.Duration() != time.Duration(60000000000) {
t.Fatalf("unexpected numeric duration: %v", d.Duration())
}
}
+106
View File
@@ -3,6 +3,7 @@ package db
import "context"
import "database/sql"
import "fmt"
import "io/fs"
import "os"
import "path/filepath"
import "sort"
@@ -239,6 +240,86 @@ func (s *Store) ApplyMigrations(dir string) error {
return nil
}
func (s *Store) ApplyMigrationsFS(fsys fs.FS, dir string) error {
var files []string
var hasSchema bool
var err error
var applied bool
var i int
var version string
var content []byte
var initPath string
err = s.ensureSchemaMigrationsTable()
if err != nil {
return err
}
files, err = listMigrationFilesFS(fsys, dir)
if err != nil {
return err
}
hasSchema, err = s.hasCoreSchema()
if err != nil {
return err
}
if len(files) == 0 {
if hasSchema {
return nil
}
return fmt.Errorf("no migration files found in %s", dir)
}
initPath, err = findMigrationPath(files, "001_init")
if err != nil {
return err
}
if !hasSchema {
content, err = fs.ReadFile(fsys, initPath)
if err != nil {
return err
}
_, err = s.Exec(string(content))
if err != nil {
return fmt.Errorf("apply %s: %w", filepath.Base(initPath), err)
}
err = s.markMigration("001_init")
if err != nil {
return err
}
}
err = s.markMigration("001_init")
if err != nil {
return err
}
for i = 0; i < len(files); i++ {
version = migrationVersionFromPath(files[i])
if version == "001_init" {
continue
}
applied, err = s.hasMigration(version)
if err != nil {
return err
}
if applied {
continue
}
content, err = fs.ReadFile(fsys, files[i])
if err != nil {
return err
}
_, err = s.Exec(string(content))
if err != nil {
return fmt.Errorf("apply %s: %w", filepath.Base(files[i]), err)
}
err = s.markMigration(version)
if err != nil {
return err
}
}
return nil
}
func (s *Store) hasCoreSchema() (bool, error) {
var row *sql.Row
var value int
@@ -340,6 +421,31 @@ func listMigrationFiles(dir string) ([]string, error) {
return files, nil
}
func listMigrationFilesFS(fsys fs.FS, dir string) ([]string, error) {
var entries []fs.DirEntry
var files []string
var err error
var i int
var name string
entries, err = fs.ReadDir(fsys, dir)
if err != nil {
return nil, err
}
for i = 0; i < len(entries); i++ {
if entries[i].IsDir() {
continue
}
name = entries[i].Name()
if !strings.HasSuffix(strings.ToLower(name), ".sql") {
continue
}
files = append(files, dir + "/" + name)
}
sort.Strings(files)
return files, nil
}
func migrationVersionFromPath(path string) string {
return strings.TrimSuffix(filepath.Base(path), filepath.Ext(path))
}
+6 -5
View File
@@ -16,6 +16,7 @@ import "codit/internal/db"
import "codit/internal/middleware"
import "codit/internal/models"
import "codit/internal/util"
import codit_logger "codit/logger"
type AuthFunc func(username, password string) (bool, error)
@@ -23,12 +24,12 @@ type HTTPServer struct {
store *db.Store
baseDir string
auth AuthFunc
logger *util.Logger
logger codit_logger.Logger
}
const logIDDocker string = "docker"
func NewHTTPServer(store *db.Store, baseDir string, auth AuthFunc, logger *util.Logger) *HTTPServer {
func NewHTTPServer(store *db.Store, baseDir string, auth AuthFunc, logger codit_logger.Logger) *HTTPServer {
return &HTTPServer{
store: store,
baseDir: baseDir,
@@ -59,7 +60,7 @@ func (s *HTTPServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if !hasLogInfo {
logInfo = RequestLogInfo{OrgPath: "-", ProjectID: "-", ProjectSlug: "-", RepoID: "-", RepoName: "-"}
}
s.logger.Write(logIDDocker, util.LOG_INFO, "method=%s path=%s orgpath=%s project_id=%s project_slug=%s repo_id=%s repo_name=%s remote=%s user=%s mtls_cn=%q mtls_issuer_cn=%q mtls_serial=%q mtls_user_id=%q mtls_username=%q mtls_profile_id=%q mtls_permissions=%q mtls_scope=%q status=%d",
s.logger.Write(logIDDocker, codit_logger.LOG_INFO, "method=%s path=%s orgpath=%s project_id=%s project_slug=%s repo_id=%s repo_name=%s remote=%s user=%s mtls_cn=%q mtls_issuer_cn=%q mtls_serial=%q mtls_user_id=%q mtls_username=%q mtls_profile_id=%q mtls_permissions=%q mtls_scope=%q status=%d",
r.Method, r.URL.Path, logInfo.OrgPath, logInfo.ProjectID, logInfo.ProjectSlug, logInfo.RepoID, logInfo.RepoName, r.RemoteAddr, userLabel, mtlsInfo.SubjectCN, mtlsInfo.IssuerCN, mtlsInfo.Serial, mtlsInfo.UserID, mtlsInfo.Username, mtlsInfo.ProfileID, mtlsInfo.Permissions, mtlsInfo.Scope, recorder.status)
}
return
@@ -74,7 +75,7 @@ func (s *HTTPServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if !hasLogInfo {
logInfo = RequestLogInfo{OrgPath: "-", ProjectID: "-", ProjectSlug: "-", RepoID: "-", RepoName: "-"}
}
s.logger.Write(logIDDocker, util.LOG_INFO, "method=%s path=%s orgpath=%s project_id=%s project_slug=%s repo_id=%s repo_name=%s remote=%s user=%s mtls_cn=%q mtls_issuer_cn=%q mtls_serial=%q mtls_user_id=%q mtls_username=%q mtls_profile_id=%q mtls_permissions=%q mtls_scope=%q status=%d",
s.logger.Write(logIDDocker, codit_logger.LOG_INFO, "method=%s path=%s orgpath=%s project_id=%s project_slug=%s repo_id=%s repo_name=%s remote=%s user=%s mtls_cn=%q mtls_issuer_cn=%q mtls_serial=%q mtls_user_id=%q mtls_username=%q mtls_profile_id=%q mtls_permissions=%q mtls_scope=%q status=%d",
r.Method, r.URL.Path, logInfo.OrgPath, logInfo.ProjectID, logInfo.ProjectSlug, logInfo.RepoID, logInfo.RepoName, r.RemoteAddr, userLabel, mtlsInfo.SubjectCN, mtlsInfo.IssuerCN, mtlsInfo.Serial, mtlsInfo.UserID, mtlsInfo.Username, mtlsInfo.ProfileID, mtlsInfo.Permissions, mtlsInfo.Scope, recorder.status)
}
return
@@ -88,7 +89,7 @@ func (s *HTTPServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if !hasLogInfo {
logInfo = RequestLogInfo{OrgPath: "-", ProjectID: "-", ProjectSlug: "-", RepoID: "-", RepoName: "-"}
}
s.logger.Write(logIDDocker, util.LOG_INFO, "method=%s path=%s orgpath=%s project_id=%s project_slug=%s repo_id=%s repo_name=%s remote=%s user=%s mtls_cn=%q mtls_issuer_cn=%q mtls_serial=%q mtls_user_id=%q mtls_username=%q mtls_profile_id=%q mtls_permissions=%q mtls_scope=%q status=%d",
s.logger.Write(logIDDocker, codit_logger.LOG_INFO, "method=%s path=%s orgpath=%s project_id=%s project_slug=%s repo_id=%s repo_name=%s remote=%s user=%s mtls_cn=%q mtls_issuer_cn=%q mtls_serial=%q mtls_user_id=%q mtls_username=%q mtls_profile_id=%q mtls_permissions=%q mtls_scope=%q status=%d",
r.Method, r.URL.Path, logInfo.OrgPath, logInfo.ProjectID, logInfo.ProjectSlug, logInfo.RepoID, logInfo.RepoName, r.RemoteAddr, userLabel, mtlsInfo.SubjectCN, mtlsInfo.IssuerCN, mtlsInfo.Serial, mtlsInfo.UserID, mtlsInfo.Username, mtlsInfo.ProfileID, mtlsInfo.Permissions, mtlsInfo.Scope, recorder.status)
}
}
+4 -4
View File
@@ -1,7 +1,7 @@
package git
import codit_logger "codit/logger"
import "codit/internal/middleware"
import "codit/internal/util"
import "net/http"
import "os"
import "path/filepath"
@@ -12,12 +12,12 @@ import "github.com/sosedoff/gitkit"
type HTTPServer struct {
handler http.Handler
baseDir string
logger *util.Logger
logger codit_logger.Logger
}
type AuthFunc func(username, password string) (bool, error)
func NewHTTPServer(baseDir string, auth AuthFunc, logger *util.Logger) (*HTTPServer, error) {
func NewHTTPServer(baseDir string, auth AuthFunc, logger codit_logger.Logger) (*HTTPServer, error) {
var cfg gitkit.Config
var srv *gitkit.Server
cfg = gitkit.Config{
@@ -74,7 +74,7 @@ func (s *HTTPServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
mtlsInfo = middleware.ClientCertLogFields(r)
s.logger.Write(
"git", util.LOG_INFO, "method=%s path=%s orgpath=%s project_id=%s project_slug=%s repo_id=%s repo_name=%s remote=%s user=%s mtls_cn=%q mtls_issuer_cn=%q mtls_serial=%q mtls_user_id=%q mtls_username=%q mtls_profile_id=%q mtls_permissions=%q mtls_scope=%q status=%d\n",
"git", codit_logger.LOG_INFO, "method=%s path=%s orgpath=%s project_id=%s project_slug=%s repo_id=%s repo_name=%s remote=%s user=%s mtls_cn=%q mtls_issuer_cn=%q mtls_serial=%q mtls_user_id=%q mtls_username=%q mtls_profile_id=%q mtls_permissions=%q mtls_scope=%q status=%d\n",
r.Method, r.URL.Path, logInfo.OrgPath, logInfo.ProjectID, logInfo.ProjectSlug, logInfo.RepoID, logInfo.RepoName, r.RemoteAddr, userLabel, mtlsInfo.SubjectCN, mtlsInfo.IssuerCN, mtlsInfo.Serial, mtlsInfo.UserID, mtlsInfo.Username, mtlsInfo.ProfileID, mtlsInfo.Permissions, mtlsInfo.Scope, recorder.status)
}
}
+31 -31
View File
@@ -1,5 +1,6 @@
package handlers
import codit_logger "codit/logger"
import "context"
import "bytes"
import "crypto/ecdsa"
@@ -21,7 +22,6 @@ import "time"
import "golang.org/x/crypto/acme"
import "codit/internal/models"
import "codit/internal/util"
type acmeProfileRequest struct {
Name string `json:"name"`
@@ -98,7 +98,7 @@ func (api *API) CreateACMEProfile(w http.ResponseWriter, r *http.Request, _ map[
WriteJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid request"})
return
}
api.Logger.Write(acmeLogID, util.LOG_INFO, "profile create requested name=%s directory=%s", strings.TrimSpace(req.Name), strings.TrimSpace(req.DirectoryURL))
api.Logger.Write(acmeLogID, codit_logger.LOG_INFO, "profile create requested name=%s directory=%s", strings.TrimSpace(req.Name), strings.TrimSpace(req.DirectoryURL))
item, err = normalizeACMEProfileRequest(req)
if err != nil {
WriteJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
@@ -108,11 +108,11 @@ func (api *API) CreateACMEProfile(w http.ResponseWriter, r *http.Request, _ map[
item.LastError = ""
item, err = api.store(r).CreateACMEProfile(item)
if err != nil {
api.Logger.Write(acmeLogID, util.LOG_WARN, "profile create failed name=%s err=%v", item.Name, err)
api.Logger.Write(acmeLogID, codit_logger.LOG_WARN, "profile create failed name=%s err=%v", item.Name, err)
WriteJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
return
}
api.Logger.Write(acmeLogID, util.LOG_INFO, "profile create success id=%s name=%s", item.ID, item.Name)
api.Logger.Write(acmeLogID, codit_logger.LOG_INFO, "profile create success id=%s name=%s", item.ID, item.Name)
out = buildACMEProfileSummary(item)
WriteJSON(w, http.StatusCreated, out)
}
@@ -157,11 +157,11 @@ func (api *API) UpdateACMEProfile(w http.ResponseWriter, r *http.Request, params
item.Enabled = req.Enabled
item, err = api.store(r).UpdateACMEProfile(item)
if err != nil {
api.Logger.Write(acmeLogID, util.LOG_WARN, "profile update failed id=%s err=%v", params["id"], err)
api.Logger.Write(acmeLogID, codit_logger.LOG_WARN, "profile update failed id=%s err=%v", params["id"], err)
WriteJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
return
}
api.Logger.Write(acmeLogID, util.LOG_INFO, "profile update success id=%s name=%s enabled=%t", item.ID, item.Name, item.Enabled)
api.Logger.Write(acmeLogID, codit_logger.LOG_INFO, "profile update success id=%s name=%s enabled=%t", item.ID, item.Name, item.Enabled)
out = buildACMEProfileSummary(item)
WriteJSON(w, http.StatusOK, out)
}
@@ -173,11 +173,11 @@ func (api *API) DeleteACMEProfile(w http.ResponseWriter, r *http.Request, params
}
err = api.store(r).DeleteACMEProfile(params["id"])
if err != nil {
api.Logger.Write(acmeLogID, util.LOG_WARN, "profile delete failed id=%s err=%v", params["id"], err)
api.Logger.Write(acmeLogID, codit_logger.LOG_WARN, "profile delete failed id=%s err=%v", params["id"], err)
WriteJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
}
api.Logger.Write(acmeLogID, util.LOG_INFO, "profile delete success id=%s", params["id"])
api.Logger.Write(acmeLogID, codit_logger.LOG_INFO, "profile delete success id=%s", params["id"])
WriteJSON(w, http.StatusOK, map[string]string{"status": "deleted"})
}
@@ -332,7 +332,7 @@ func (api *API) RenewPKICertWithACME(w http.ResponseWriter, r *http.Request, par
}
sans = append(sans, part)
}
api.Logger.Write(acmeLogID, util.LOG_INFO, "order renew requested cert_id=%s profile=%s cn=%s", cert.ID, prev.ProfileID, cert.CommonName)
api.Logger.Write(acmeLogID, codit_logger.LOG_INFO, "order renew requested cert_id=%s profile=%s cn=%s", cert.ID, prev.ProfileID, cert.CommonName)
order, err = api.createACMEOrder(r.Context(), prev.ProfileID, cert.CommonName, sans, cert.ID)
if err != nil {
if err == sql.ErrNoRows {
@@ -362,7 +362,7 @@ func (api *API) createACMEOrder(ctx context.Context, profileID string, commonNam
var certKeyPEM string
var csrPEM string
var err error
api.Logger.Write(acmeLogID, util.LOG_INFO, "order create requested profile=%s cn=%s san_count=%d", strings.TrimSpace(profileID), strings.TrimSpace(commonName), len(sanDNS))
api.Logger.Write(acmeLogID, codit_logger.LOG_INFO, "order create requested profile=%s cn=%s san_count=%d", strings.TrimSpace(profileID), strings.TrimSpace(commonName), len(sanDNS))
if strings.TrimSpace(profileID) == "" {
return order, errors.New("profile_id is required")
}
@@ -389,19 +389,19 @@ func (api *API) createACMEOrder(ctx context.Context, profileID string, commonNam
}
client, accountKey, profile, err = api.ensureACMEAccount(ctx, profile)
if err != nil {
api.Logger.Write(acmeLogID, util.LOG_WARN, "order create failed profile=%s step=account err=%v", profile.ID, err)
api.Logger.Write(acmeLogID, codit_logger.LOG_WARN, "order create failed profile=%s step=account err=%v", profile.ID, err)
_ = api.storeContext(ctx).UpdateACMEProfileLastError(profile.ID, err.Error())
return order, err
}
_ = accountKey
api.Logger.Write(acmeLogID, util.LOG_INFO, "order create account ready profile=%s account=%s", profile.ID, profile.AccountURL)
api.Logger.Write(acmeLogID, codit_logger.LOG_INFO, "order create account ready profile=%s account=%s", profile.ID, profile.AccountURL)
rawOrder, err = client.AuthorizeOrder(context.Background(), domainIDs)
if err != nil {
api.Logger.Write(acmeLogID, util.LOG_WARN, "order create failed profile=%s step=authorize_order err=%v", profile.ID, err)
api.Logger.Write(acmeLogID, codit_logger.LOG_WARN, "order create failed profile=%s step=authorize_order err=%v", profile.ID, err)
_ = api.storeContext(ctx).UpdateACMEProfileLastError(profile.ID, err.Error())
return order, err
}
api.Logger.Write(acmeLogID, util.LOG_INFO, "order created upstream profile=%s order_url=%s authz_count=%d", profile.ID, rawOrder.URI, len(rawOrder.AuthzURLs))
api.Logger.Write(acmeLogID, codit_logger.LOG_INFO, "order created upstream profile=%s order_url=%s authz_count=%d", profile.ID, rawOrder.URI, len(rawOrder.AuthzURLs))
for i = 0; i < len(rawOrder.AuthzURLs); i++ {
authz, err = client.GetAuthorization(context.Background(), rawOrder.AuthzURLs[i])
if err != nil {
@@ -430,7 +430,7 @@ func (api *API) createACMEOrder(ctx context.Context, profileID string, commonNam
DNSValue: txt,
Status: ch.Status,
})
api.Logger.Write(acmeLogID, util.LOG_INFO, "dns challenge prepared order=%s domain=%s dns_name=%s", rawOrder.URI, authz.Identifier.Value, acmeDNSRecordName(authz.Identifier.Value))
api.Logger.Write(acmeLogID, codit_logger.LOG_INFO, "dns challenge prepared order=%s domain=%s dns_name=%s", rawOrder.URI, authz.Identifier.Value, acmeDNSRecordName(authz.Identifier.Value))
}
order = models.ACMEOrder{
ProfileID: profile.ID,
@@ -447,10 +447,10 @@ func (api *API) createACMEOrder(ctx context.Context, profileID string, commonNam
}
order, err = api.storeContext(ctx).CreateACMEOrder(order)
if err != nil {
api.Logger.Write(acmeLogID, util.LOG_WARN, "order create persist failed profile=%s order_url=%s err=%v", profile.ID, rawOrder.URI, err)
api.Logger.Write(acmeLogID, codit_logger.LOG_WARN, "order create persist failed profile=%s order_url=%s err=%v", profile.ID, rawOrder.URI, err)
return order, err
}
api.Logger.Write(acmeLogID, util.LOG_INFO, "order create success id=%s profile=%s cn=%s challenge_count=%d", order.ID, order.ProfileID, order.CommonName, len(order.Challenges))
api.Logger.Write(acmeLogID, codit_logger.LOG_INFO, "order create success id=%s profile=%s cn=%s challenge_count=%d", order.ID, order.ProfileID, order.CommonName, len(order.Challenges))
_ = api.storeContext(ctx).UpdateACMEProfileLastError(profile.ID, "")
return order, nil
}
@@ -497,7 +497,7 @@ func (api *API) FinalizeACMEOrder(w http.ResponseWriter, r *http.Request, params
WriteJSON(w, http.StatusBadRequest, map[string]string{"error": "renew_mode must be override or new_cert"})
return
}
api.Logger.Write(acmeLogID, util.LOG_INFO, "order finalize requested id=%s", params["id"])
api.Logger.Write(acmeLogID, codit_logger.LOG_INFO, "order finalize requested id=%s", params["id"])
order, err = api.store(r).GetACMEOrder(params["id"])
if err != nil {
if err == sql.ErrNoRows {
@@ -518,7 +518,7 @@ func (api *API) FinalizeACMEOrder(w http.ResponseWriter, r *http.Request, params
}
client, accountKey, profile, err = api.ensureACMEAccount(r.Context(), profile)
if err != nil {
api.Logger.Write(acmeLogID, util.LOG_WARN, "order finalize failed id=%s step=account err=%v", order.ID, err)
api.Logger.Write(acmeLogID, codit_logger.LOG_WARN, "order finalize failed id=%s step=account err=%v", order.ID, err)
order.Status = "failed"
order.LastError = err.Error()
_, _ = api.store(r).UpdateACMEOrder(order)
@@ -538,13 +538,13 @@ func (api *API) FinalizeACMEOrder(w http.ResponseWriter, r *http.Request, params
}
if authz.Status == acme.StatusValid {
order.Challenges[i].Status = acme.StatusValid
api.Logger.Write(acmeLogID, util.LOG_INFO, "challenge already valid order=%s domain=%s", order.ID, order.Challenges[i].Identifier)
api.Logger.Write(acmeLogID, codit_logger.LOG_INFO, "challenge already valid order=%s domain=%s", order.ID, order.Challenges[i].Identifier)
continue
}
if profile.SolverType == "acme_dns" {
err = api.updateACMEDNSRecord(profile, order.Challenges[i])
if err != nil {
api.Logger.Write(acmeLogID, util.LOG_WARN, "challenge dns update failed order=%s domain=%s err=%v", order.ID, order.Challenges[i].Identifier, err)
api.Logger.Write(acmeLogID, codit_logger.LOG_WARN, "challenge dns update failed order=%s domain=%s err=%v", order.ID, order.Challenges[i].Identifier, err)
order.Status = "failed"
order.LastError = err.Error()
order.Challenges[i].Status = "failed"
@@ -553,7 +553,7 @@ func (api *API) FinalizeACMEOrder(w http.ResponseWriter, r *http.Request, params
WriteJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
return
}
api.Logger.Write(acmeLogID, util.LOG_INFO, "challenge dns updated order=%s domain=%s dns_name=%s", order.ID, order.Challenges[i].Identifier, order.Challenges[i].DNSName)
api.Logger.Write(acmeLogID, codit_logger.LOG_INFO, "challenge dns updated order=%s domain=%s dns_name=%s", order.ID, order.Challenges[i].Identifier, order.Challenges[i].DNSName)
}
challenge = &acme.Challenge{Type: "dns-01", URI: order.Challenges[i].ChallengeURL, Token: order.Challenges[i].Token}
challenge, err = client.Accept(context.Background(), challenge)
@@ -567,7 +567,7 @@ func (api *API) FinalizeACMEOrder(w http.ResponseWriter, r *http.Request, params
}
authz, err = client.WaitAuthorization(context.Background(), order.Challenges[i].AuthorizationURL)
if err != nil {
api.Logger.Write(acmeLogID, util.LOG_WARN, "challenge wait failed order=%s domain=%s err=%v", order.ID, order.Challenges[i].Identifier, err)
api.Logger.Write(acmeLogID, codit_logger.LOG_WARN, "challenge wait failed order=%s domain=%s err=%v", order.ID, order.Challenges[i].Identifier, err)
order.Status = "failed"
order.LastError = err.Error()
order.Challenges[i].Status = "failed"
@@ -582,11 +582,11 @@ func (api *API) FinalizeACMEOrder(w http.ResponseWriter, r *http.Request, params
break
}
}
api.Logger.Write(acmeLogID, util.LOG_INFO, "challenge finalized order=%s domain=%s status=%s", order.ID, order.Challenges[i].Identifier, order.Challenges[i].Status)
api.Logger.Write(acmeLogID, codit_logger.LOG_INFO, "challenge finalized order=%s domain=%s status=%s", order.ID, order.Challenges[i].Identifier, order.Challenges[i].Status)
}
certDER, certURL, err = client.CreateOrderCert(context.Background(), order.FinalizeURL, decodeCSRPEM(order.CSRPEM), true)
if err != nil {
api.Logger.Write(acmeLogID, util.LOG_WARN, "order finalize failed id=%s step=create_cert err=%v", order.ID, err)
api.Logger.Write(acmeLogID, codit_logger.LOG_WARN, "order finalize failed id=%s step=create_cert err=%v", order.ID, err)
order.Status = "failed"
order.LastError = err.Error()
_, _ = api.store(r).UpdateACMEOrder(order)
@@ -647,20 +647,20 @@ func (api *API) FinalizeACMEOrder(w http.ResponseWriter, r *http.Request, params
cert.CAID = existing.CAID
cert, err = api.store(r).ReplacePKICert(cert)
if err != nil {
api.Logger.Write(acmeLogID, util.LOG_WARN, "order finalize failed id=%s step=replace_cert err=%v", order.ID, err)
api.Logger.Write(acmeLogID, codit_logger.LOG_WARN, "order finalize failed id=%s step=replace_cert err=%v", order.ID, err)
order.Status = "failed"
order.LastError = err.Error()
_, _ = api.store(r).UpdateACMEOrder(order)
WriteJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
return
}
api.Logger.Write(acmeLogID, util.LOG_INFO, "order finalize replaced existing cert id=%s", cert.ID)
api.Logger.Write(acmeLogID, codit_logger.LOG_INFO, "order finalize replaced existing cert id=%s", cert.ID)
}
}
if strings.TrimSpace(cert.ID) == "" {
cert, err = api.store(r).CreatePKICert(cert)
if err != nil {
api.Logger.Write(acmeLogID, util.LOG_WARN, "order finalize failed id=%s step=store_cert err=%v", order.ID, err)
api.Logger.Write(acmeLogID, codit_logger.LOG_WARN, "order finalize failed id=%s step=store_cert err=%v", order.ID, err)
order.Status = "failed"
order.LastError = err.Error()
_, _ = api.store(r).UpdateACMEOrder(order)
@@ -678,7 +678,7 @@ func (api *API) FinalizeACMEOrder(w http.ResponseWriter, r *http.Request, params
}
_ = api.store(r).UpdateACMEProfileLastError(profile.ID, "")
_ = certURL
api.Logger.Write(acmeLogID, util.LOG_INFO, "order finalize success id=%s cert_id=%s cert_url=%s", order.ID, order.CertID, certURL)
api.Logger.Write(acmeLogID, codit_logger.LOG_INFO, "order finalize success id=%s cert_id=%s cert_url=%s", order.ID, order.CertID, certURL)
WriteJSON(w, http.StatusOK, order)
}
@@ -689,11 +689,11 @@ func (api *API) DeleteACMEOrder(w http.ResponseWriter, r *http.Request, params m
}
err = api.store(r).DeleteACMEOrder(params["id"])
if err != nil {
api.Logger.Write(acmeLogID, util.LOG_WARN, "order delete failed id=%s err=%v", params["id"], err)
api.Logger.Write(acmeLogID, codit_logger.LOG_WARN, "order delete failed id=%s err=%v", params["id"], err)
WriteJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
}
api.Logger.Write(acmeLogID, util.LOG_INFO, "order delete success id=%s", params["id"])
api.Logger.Write(acmeLogID, codit_logger.LOG_INFO, "order delete success id=%s", params["id"])
WriteJSON(w, http.StatusOK, map[string]string{"status": "deleted"})
}
+16 -15
View File
@@ -19,7 +19,7 @@ import "time"
import "unicode"
import "codit/internal/auth"
import "codit/internal/config"
import "codit/config"
import "codit/internal/db"
import "codit/internal/docker"
import "codit/internal/git"
@@ -28,6 +28,7 @@ import "codit/internal/models"
import "codit/internal/rpm"
import "codit/internal/storage"
import "codit/internal/util"
import codit_logger "codit/logger"
type API struct {
Cfg config.Config
@@ -38,7 +39,7 @@ type API struct {
RpmMirror *rpm.MirrorManager
DockerBase string
Uploads storage.FileStore
Logger *util.Logger
Logger codit_logger.Logger
SSHSessionRegistry *SSHSessionRegistry
SSHPromptedAuthStore *SSHPromptedAuthStore
SSHPreparedSessionStore *SSHPreparedSessionStore
@@ -386,10 +387,10 @@ func (api *API) Login(w http.ResponseWriter, r *http.Request, _ map[string]strin
authCfg, _ = api.effectiveAuthConfig(r.Context())
if authCfg.AuthMode == "ldap" || authCfg.AuthMode == "hybrid" {
api.Logger.Write(logIDAuth, util.LOG_INFO, "ldap login attempt username=%s mode=%s", req.Username, authCfg.AuthMode)
api.Logger.Write(logIDAuth, codit_logger.LOG_INFO, "ldap login attempt username=%s mode=%s", req.Username, authCfg.AuthMode)
ldapUser, err = auth.LDAPAuthenticateContext(r.Context(), authCfg, req.Username, req.Password)
if err == nil {
api.Logger.Write(logIDAuth, util.LOG_INFO, "ldap login success username=%s", req.Username)
api.Logger.Write(logIDAuth, codit_logger.LOG_INFO, "ldap login success username=%s", req.Username)
if user.ID != "" && user.Disabled {
WriteJSON(w, http.StatusForbidden, map[string]string{"error": "user disabled"})
return
@@ -413,7 +414,7 @@ func (api *API) Login(w http.ResponseWriter, r *http.Request, _ map[string]strin
WriteJSON(w, http.StatusOK, user)
return
}
api.Logger.Write(logIDAuth, util.LOG_WARN, "ldap login failed username=%s err=%v", req.Username, err)
api.Logger.Write(logIDAuth, codit_logger.LOG_WARN, "ldap login failed username=%s err=%v", req.Username, err)
}
WriteJSON(w, http.StatusUnauthorized, map[string]string{"error": "invalid credentials"})
@@ -822,15 +823,15 @@ func (api *API) GetAuthSettings(w http.ResponseWriter, r *http.Request, _ map[st
}
user, ok = middleware.UserFromContext(r.Context())
if ok {
api.Logger.Write(logIDAuth, util.LOG_INFO, "auth settings fetch requested by user=%s", user.Username)
api.Logger.Write(logIDAuth, codit_logger.LOG_INFO, "auth settings fetch requested by user=%s", user.Username)
}
settings, err = api.getMergedAuthSettings(r.Context())
if err != nil {
api.Logger.Write(logIDAuth, util.LOG_ERROR, "auth settings fetch failed err=%v", err)
api.Logger.Write(logIDAuth, codit_logger.LOG_ERROR, "auth settings fetch failed err=%v", err)
WriteJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
}
api.Logger.Write(logIDAuth, util.LOG_INFO, "auth settings fetch success mode=%s ldap_url=%s oidc_authorize_url=%s", settings.AuthMode, settings.LDAPURL, settings.OIDCAuthorizeURL)
api.Logger.Write(logIDAuth, codit_logger.LOG_INFO, "auth settings fetch success mode=%s ldap_url=%s oidc_authorize_url=%s", settings.AuthMode, settings.LDAPURL, settings.OIDCAuthorizeURL)
WriteJSON(w, http.StatusOK, settings)
}
@@ -874,16 +875,16 @@ func (api *API) UpdateAuthSettings(w http.ResponseWriter, r *http.Request, _ map
settings.OIDCScopes = "openid profile email"
}
if ok {
api.Logger.Write(logIDAuth, util.LOG_INFO, "auth settings update requested by user=%s mode=%s oidc_enabled=%t ldap_url=%s oidc_authorize_url=%s",
api.Logger.Write(logIDAuth, codit_logger.LOG_INFO, "auth settings update requested by user=%s mode=%s oidc_enabled=%t ldap_url=%s oidc_authorize_url=%s",
user.Username, settings.AuthMode, settings.OIDCEnabled, settings.LDAPURL, settings.OIDCAuthorizeURL)
}
err = api.store(r).SetAuthSettings(settings)
if err != nil {
api.Logger.Write(logIDAuth, util.LOG_ERROR, "auth settings update failed err=%v", err)
api.Logger.Write(logIDAuth, codit_logger.LOG_ERROR, "auth settings update failed err=%v", err)
WriteJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
}
api.Logger.Write(logIDAuth, util.LOG_INFO, "auth settings update success mode=%s oidc_enabled=%t ldap_url=%s oidc_authorize_url=%s", settings.AuthMode, settings.OIDCEnabled, settings.LDAPURL, settings.OIDCAuthorizeURL)
api.Logger.Write(logIDAuth, codit_logger.LOG_INFO, "auth settings update success mode=%s oidc_enabled=%t ldap_url=%s oidc_authorize_url=%s", settings.AuthMode, settings.OIDCEnabled, settings.LDAPURL, settings.OIDCAuthorizeURL)
WriteJSON(w, http.StatusOK, settings)
}
@@ -939,19 +940,19 @@ func (api *API) TestLDAPSettings(w http.ResponseWriter, r *http.Request, _ map[s
cfg.LDAPUserFilter = merged.LDAPUserFilter
err = auth.LDAPTestConnectionContext(r.Context(), cfg)
if err != nil {
api.Logger.Write(logIDAuth, util.LOG_WARN, "ldap test connection failed url=%s err=%v", cfg.LDAPURL, err)
api.Logger.Write(logIDAuth, codit_logger.LOG_WARN, "ldap test connection failed url=%s err=%v", cfg.LDAPURL, err)
WriteJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
return
}
api.Logger.Write(logIDAuth, util.LOG_INFO, "ldap test connection success url=%s", cfg.LDAPURL)
api.Logger.Write(logIDAuth, codit_logger.LOG_INFO, "ldap test connection success url=%s", cfg.LDAPURL)
if strings.TrimSpace(req.Username) != "" && strings.TrimSpace(req.Password) != "" {
user, err = auth.LDAPAuthenticateContext(r.Context(), cfg, strings.TrimSpace(req.Username), req.Password)
if err != nil {
api.Logger.Write(logIDAuth, util.LOG_WARN, "ldap test bind failed username=%s err=%v", strings.TrimSpace(req.Username), err)
api.Logger.Write(logIDAuth, codit_logger.LOG_WARN, "ldap test bind failed username=%s err=%v", strings.TrimSpace(req.Username), err)
WriteJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
return
}
api.Logger.Write(logIDAuth, util.LOG_INFO, "ldap test bind success username=%s", user.Username)
api.Logger.Write(logIDAuth, codit_logger.LOG_INFO, "ldap test bind success username=%s", user.Username)
WriteJSON(w, http.StatusOK, map[string]string{"status": "ok", "user": user.Username})
return
}
+7 -7
View File
@@ -1,5 +1,6 @@
package handlers
import codit_logger "codit/logger"
import "context"
import "crypto/rand"
import "crypto/sha1"
@@ -16,9 +17,8 @@ import "net/url"
import "strings"
import "time"
import "codit/internal/config"
import "codit/config"
import "codit/internal/models"
import "codit/internal/util"
type oidcTokenResponse struct {
AccessToken string `json:"access_token"`
@@ -91,7 +91,7 @@ func (api *API) OIDCLogin(w http.ResponseWriter, r *http.Request, _ map[string]s
SameSite: http.SameSiteLaxMode,
})
authURL = api.buildOIDCAuthorizeURL(cfg, state)
api.Logger.Write(logIDAuth, util.LOG_INFO, "oidc login redirect authorize_url=%s", cfg.OIDCAuthorizeURL)
api.Logger.Write(logIDAuth, codit_logger.LOG_INFO, "oidc login redirect authorize_url=%s", cfg.OIDCAuthorizeURL)
http.Redirect(w, r, authURL, http.StatusFound)
}
@@ -137,24 +137,24 @@ func (api *API) OIDCCallback(w http.ResponseWriter, r *http.Request, _ map[strin
}
token, err = api.oidcExchangeCode(r.Context(), cfg, code)
if err != nil {
api.Logger.Write(logIDAuth, util.LOG_WARN, "oidc token exchange failed err=%v", err)
api.Logger.Write(logIDAuth, codit_logger.LOG_WARN, "oidc token exchange failed err=%v", err)
WriteJSON(w, http.StatusUnauthorized, map[string]string{"error": err.Error()})
return
}
claims, err = api.oidcResolveClaims(r.Context(), cfg, token)
if err != nil {
api.Logger.Write(logIDAuth, util.LOG_WARN, "oidc claims fetch failed err=%v", err)
api.Logger.Write(logIDAuth, codit_logger.LOG_WARN, "oidc claims fetch failed err=%v", err)
WriteJSON(w, http.StatusUnauthorized, map[string]string{"error": "oidc claims fetch failed"})
return
}
user, err = api.oidcGetOrCreateUser(r.Context(), claims)
if err != nil {
api.Logger.Write(logIDAuth, util.LOG_WARN, "oidc user mapping failed err=%v", err)
api.Logger.Write(logIDAuth, codit_logger.LOG_WARN, "oidc user mapping failed err=%v", err)
WriteJSON(w, http.StatusUnauthorized, map[string]string{"error": "oidc user mapping failed"})
return
}
api.issueSession(w, r, user)
api.Logger.Write(logIDAuth, util.LOG_INFO, "oidc login success username=%s", user.Username)
api.Logger.Write(logIDAuth, codit_logger.LOG_INFO, "oidc login success username=%s", user.Username)
http.Redirect(w, r, "/", http.StatusFound)
}
+2 -2
View File
@@ -1,5 +1,6 @@
package handlers
import codit_logger "codit/logger"
import "archive/zip"
import "bytes"
import "context"
@@ -27,7 +28,6 @@ import "time"
import "codit/internal/middleware"
import "codit/internal/models"
import "codit/internal/pkiutil"
import "codit/internal/util"
type pkiCreateRootRequest struct {
Name string `json:"name"`
@@ -1058,7 +1058,7 @@ func (api *API) DownloadPKICertBundle(w http.ResponseWriter, r *http.Request, pa
chainCount++
}
}
api.Logger.Write(logIDPKI, util.LOG_INFO, "cert bundle cert=%s ca_id=%s ca_chain_entries=%d expected_cert_blocks=%d", cert.ID, cert.CAID, chainCount, 1+chainCount)
api.Logger.Write(logIDPKI, codit_logger.LOG_INFO, "cert bundle cert=%s ca_id=%s ca_chain_entries=%d expected_cert_blocks=%d", cert.ID, cert.CAID, chainCount, 1+chainCount)
data, err = buildPKIZipBundle(cert.CommonName, cert.CertPEM, cert.KeyPEM, false, caPEMs)
if err != nil {
WriteJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
@@ -1,5 +1,6 @@
package handlers
import codit_logger "codit/logger"
import "crypto/rand"
import "crypto/rsa"
import "crypto/x509"
@@ -20,7 +21,6 @@ import "time"
import "codit/internal/middleware"
import "codit/internal/models"
import "codit/internal/pkiutil"
import "codit/internal/util"
const logIDPKI string = "pki"
const pkiClientMinValidSeconds int64 = 60
@@ -215,11 +215,11 @@ func (api *API) CreatePKIClientProfile(w http.ResponseWriter, r *http.Request, _
}
item, err = api.store(r).CreatePKIClientProfile(item)
if err != nil {
api.Logger.Write(logIDPKI, util.LOG_WARN, "client profile create failed name=%s err=%v", item.Name, err)
api.Logger.Write(logIDPKI, codit_logger.LOG_WARN, "client profile create failed name=%s err=%v", item.Name, err)
WriteJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
return
}
api.Logger.Write(logIDPKI, util.LOG_INFO, "client profile create success id=%s name=%s targets=%d", item.ID, item.Name, len(item.Targets))
api.Logger.Write(logIDPKI, codit_logger.LOG_INFO, "client profile create success id=%s name=%s targets=%d", item.ID, item.Name, len(item.Targets))
WriteJSON(w, http.StatusCreated, buildPKIClientProfileSummary(item))
}
@@ -298,11 +298,11 @@ func (api *API) UpdatePKIClientProfile(w http.ResponseWriter, r *http.Request, p
}
existing, err = api.store(r).UpdatePKIClientProfile(existing)
if err != nil {
api.Logger.Write(logIDPKI, util.LOG_WARN, "client profile update failed id=%s err=%v", params["id"], err)
api.Logger.Write(logIDPKI, codit_logger.LOG_WARN, "client profile update failed id=%s err=%v", params["id"], err)
WriteJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
return
}
api.Logger.Write(logIDPKI, util.LOG_INFO, "client profile update success id=%s name=%s", existing.ID, existing.Name)
api.Logger.Write(logIDPKI, codit_logger.LOG_INFO, "client profile update success id=%s name=%s", existing.ID, existing.Name)
WriteJSON(w, http.StatusOK, buildPKIClientProfileSummary(existing))
}
@@ -314,11 +314,11 @@ func (api *API) DeletePKIClientProfile(w http.ResponseWriter, r *http.Request, p
}
err = api.store(r).DeletePKIClientProfile(params["id"])
if err != nil {
api.Logger.Write(logIDPKI, util.LOG_WARN, "client profile delete failed id=%s err=%v", params["id"], err)
api.Logger.Write(logIDPKI, codit_logger.LOG_WARN, "client profile delete failed id=%s err=%v", params["id"], err)
WriteJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
return
}
api.Logger.Write(logIDPKI, util.LOG_INFO, "client profile delete success id=%s", params["id"])
api.Logger.Write(logIDPKI, codit_logger.LOG_INFO, "client profile delete success id=%s", params["id"])
WriteJSON(w, http.StatusOK, map[string]string{"status": "deleted"})
}
@@ -456,7 +456,7 @@ func (api *API) IssuePKIClientCertForSelf(w http.ResponseWriter, r *http.Request
serialHex = strconv.FormatInt(serial, 16)
certPEM, keyPEM, notBefore, notAfter, err = issuePKIClientCertFromProfile(ca, serial, user, profile, permissions, commonName, sanDNS, sanIPs, sanURI, validSeconds)
if err != nil {
api.Logger.Write(logIDPKI, util.LOG_WARN, "client cert issue failed user=%s profile=%s err=%v", user.Username, profile.ID, err)
api.Logger.Write(logIDPKI, codit_logger.LOG_WARN, "client cert issue failed user=%s profile=%s err=%v", user.Username, profile.ID, err)
WriteJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
return
}
@@ -512,7 +512,7 @@ func (api *API) IssuePKIClientCertForSelf(w http.ResponseWriter, r *http.Request
WriteJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
}
api.Logger.Write(logIDPKI, util.LOG_INFO, "client cert issue success user=%s profile=%s cert=%s perms=%s scope=%s", user.Username, profile.ID, cert.ID, profile.AuthzPermissions, profile.AuthzScope)
api.Logger.Write(logIDPKI, codit_logger.LOG_INFO, "client cert issue success user=%s profile=%s cert=%s perms=%s scope=%s", user.Username, profile.ID, cert.ID, profile.AuthzPermissions, profile.AuthzScope)
WriteJSON(w, http.StatusCreated, pkiClientIssueResponse{
Issuance: buildPKIClientIssuanceSummary(issuance),
CertPEM: cert.CertPEM,
@@ -672,7 +672,7 @@ func (api *API) RevokePKIClientCertForSelf(w http.ResponseWriter, r *http.Reques
WriteJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
}
api.Logger.Write(logIDPKI, util.LOG_INFO, "client cert revoke success user=%s cert=%s", user.Username, cert.ID)
api.Logger.Write(logIDPKI, codit_logger.LOG_INFO, "client cert revoke success user=%s cert=%s", user.Username, cert.ID)
WriteJSON(w, http.StatusOK, map[string]string{"status": "revoked"})
}
+13 -13
View File
@@ -1,5 +1,6 @@
package handlers
import codit_logger "codit/logger"
import "database/sql"
import "encoding/base64"
import "errors"
@@ -19,7 +20,6 @@ import "golang.org/x/net/websocket"
import "codit/internal/db"
import "codit/internal/middleware"
import "codit/internal/models"
import "codit/internal/util"
const logIDSSHBroker string = "ssh-broker"
type sshServerRequest struct {
@@ -122,7 +122,7 @@ const sshSecondFactorNone string = "none"
const sshSecondFactorPromptedTOTP string = "prompted_totp"
func (api *API) logSSHSessionConnectStart(sessionItem models.SSHSession, profile models.SSHAccessProfile, user models.User) {
api.Logger.Write(logIDSSHBroker, util.LOG_INFO,
api.Logger.Write(logIDSSHBroker, codit_logger.LOG_INFO,
"connect start session=%s requester=%s user=%s profile=%s profile_name=%q server=%s server_name=%q target=%s:%d remote_user=%s auth=%s",
sessionItem.ID,
sessionItem.RemoteAddr,
@@ -138,7 +138,7 @@ func (api *API) logSSHSessionConnectStart(sessionItem models.SSHSession, profile
}
func (api *API) logSSHSessionPrepare(sessionID string, remoteAddr string, user models.User, profile models.SSHAccessProfile) {
api.Logger.Write(logIDSSHBroker, util.LOG_INFO,
api.Logger.Write(logIDSSHBroker, codit_logger.LOG_INFO,
"connect prepare session=%s requester=%s user=%s profile=%s profile_name=%q server=%s server_name=%q target=%s:%d remote_user=%s auth=%s",
sessionID,
remoteAddr,
@@ -154,7 +154,7 @@ func (api *API) logSSHSessionPrepare(sessionID string, remoteAddr string, user m
}
func (api *API) logSSHSessionConnectSuccess(sessionItem models.SSHSession, profile models.SSHAccessProfile, user models.User, hostKeyFingerprint string) {
api.Logger.Write(logIDSSHBroker, util.LOG_INFO,
api.Logger.Write(logIDSSHBroker, codit_logger.LOG_INFO,
"connect success session=%s requester=%s user=%s profile=%s profile_name=%q server=%s server_name=%q target=%s:%d remote_user=%s auth=%s host_key=%s",
sessionItem.ID,
sessionItem.RemoteAddr,
@@ -171,7 +171,7 @@ func (api *API) logSSHSessionConnectSuccess(sessionItem models.SSHSession, profi
}
func (api *API) logSSHSessionConnectFailure(sessionItem models.SSHSession, profile models.SSHAccessProfile, user models.User, stage string, err error) {
api.Logger.Write(logIDSSHBroker, util.LOG_WARN,
api.Logger.Write(logIDSSHBroker, codit_logger.LOG_WARN,
"connect failed session=%s requester=%s user=%s profile=%s profile_name=%q server=%s server_name=%q target=%s:%d remote_user=%s auth=%s stage=%s err=%v",
sessionItem.ID,
sessionItem.RemoteAddr,
@@ -189,7 +189,7 @@ func (api *API) logSSHSessionConnectFailure(sessionItem models.SSHSession, profi
}
func (api *API) logSSHSessionPrepareFailure(sessionID string, remoteAddr string, username string, profileID string, stage string, err error) {
api.Logger.Write(logIDSSHBroker, util.LOG_WARN,
api.Logger.Write(logIDSSHBroker, codit_logger.LOG_WARN,
"connect failed session=%s requester=%s user=%s profile=%s stage=%s err=%v",
sessionID,
remoteAddr,
@@ -206,7 +206,7 @@ func (api *API) logSSHSessionClosed(sessionItem models.SSHSession, profile model
if connectedAt > 0 && endedAt >= connectedAt {
durationSeconds = endedAt - connectedAt
}
api.Logger.Write(logIDSSHBroker, util.LOG_INFO,
api.Logger.Write(logIDSSHBroker, codit_logger.LOG_INFO,
"session closed session=%s requester=%s user=%s profile=%s profile_name=%q server=%s server_name=%q target=%s:%d remote_user=%s auth=%s status=%s dur_s=%d reason=%q",
sessionItem.ID,
sessionItem.RemoteAddr,
@@ -1860,7 +1860,7 @@ event_loop:
for {
select {
case err = <-controlErrCh:
api.Logger.Write(logIDSSHBroker, util.LOG_WARN,
api.Logger.Write(logIDSSHBroker, codit_logger.LOG_WARN,
"workspace stream closed requester=%s user=%s err=%v",
remoteAddr,
user.Username,
@@ -1920,7 +1920,7 @@ event_loop:
InputErrCh: make(chan error, 1),
}
attachments[msg.SessionID] = attachment
api.Logger.Write(logIDSSHBroker, util.LOG_INFO,
api.Logger.Write(logIDSSHBroker, codit_logger.LOG_INFO,
"workspace attach session=%s requester=%s user=%s",
msg.SessionID,
remoteAddr,
@@ -2172,7 +2172,7 @@ func (api *API) runSSHSessionStream(store *db.Store, user models.User, sessionIt
}
transcriptRecorder, err = api.openSSHSessionTranscriptRecorder(sessionItem.ID)
if err != nil {
api.Logger.Write(logIDSSHBroker, util.LOG_WARN,
api.Logger.Write(logIDSSHBroker, codit_logger.LOG_WARN,
"transcript open failed session=%s err=%v",
sessionItem.ID,
err)
@@ -2225,7 +2225,7 @@ func (api *API) runSSHSessionStream(store *db.Store, user models.User, sessionIt
}
}
}
api.Logger.Write(logIDSSHBroker, util.LOG_INFO,
api.Logger.Write(logIDSSHBroker, codit_logger.LOG_INFO,
"session loop ended session=%s requester=%s user=%s profile=%s profile_name=%q source=%s wait_err=%v",
sessionItem.ID,
sessionItem.RemoteAddr,
@@ -2247,7 +2247,7 @@ func (api *API) runSSHSessionStream(store *db.Store, user models.User, sessionIt
}
if waitErr != nil && !isSSHSessionTerminalExit(waitErr) {
_ = store.UpdateSSHSessionStatus(sessionItem.ID, "error", connectedFingerprint, connectedAt, now, waitErr.Error())
api.Logger.Write(logIDSSHBroker, util.LOG_WARN,
api.Logger.Write(logIDSSHBroker, codit_logger.LOG_WARN,
"session error session=%s requester=%s user=%s profile=%s profile_name=%q server=%s server_name=%q target=%s:%d remote_user=%s auth=%s dur_s=%d err=%v",
sessionItem.ID,
sessionItem.RemoteAddr,
@@ -2268,7 +2268,7 @@ func (api *API) runSSHSessionStream(store *db.Store, user models.User, sessionIt
return
}
if isSSHSessionTerminalExit(waitErr) {
api.Logger.Write(logIDSSHBroker, util.LOG_INFO,
api.Logger.Write(logIDSSHBroker, codit_logger.LOG_INFO,
"session ended session=%s requester=%s user=%s profile=%s profile_name=%q err=%v",
sessionItem.ID,
sessionItem.RemoteAddr,
@@ -1,5 +1,6 @@
package handlers
import codit_logger "codit/logger"
import "database/sql"
import "fmt"
import "io"
@@ -11,7 +12,6 @@ import "sync"
import "codit/internal/middleware"
import "codit/internal/models"
import "codit/internal/util"
type sshSessionTranscriptRecorder struct {
mu sync.Mutex
@@ -201,7 +201,7 @@ func (api *API) writeSSHSessionTranscriptChunk(sessionID string, data []byte, re
}
err = recorder.Write(data)
if err != nil {
api.Logger.Write(logIDSSHBroker, util.LOG_WARN,
api.Logger.Write(logIDSSHBroker, codit_logger.LOG_WARN,
"transcript write failed session=%s err=%v",
sessionID,
err)
@@ -1,5 +1,6 @@
package handlers
import codit_logger "codit/logger"
import "database/sql"
import "errors"
import "net/http"
@@ -8,7 +9,6 @@ import "time"
import "codit/internal/middleware"
import "codit/internal/models"
import "codit/internal/util"
type sshPrincipalGrantTargetRequest struct {
TargetType string `json:"target_type"`
@@ -184,11 +184,11 @@ func (api *API) CreateSSHPrincipalGrant(w http.ResponseWriter, r *http.Request,
}
item, err = api.store(r).CreateSSHPrincipalGrant(item)
if err != nil {
api.Logger.Write(logIDSSHCA, util.LOG_WARN, "grant create failed name=%s err=%v", name, err)
api.Logger.Write(logIDSSHCA, codit_logger.LOG_WARN, "grant create failed name=%s err=%v", name, err)
WriteJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
return
}
api.Logger.Write(logIDSSHCA, util.LOG_INFO, "grant create success id=%s name=%s principals=%d targets=%d", item.ID, item.Name, len(item.Principals), len(item.Targets))
api.Logger.Write(logIDSSHCA, codit_logger.LOG_INFO, "grant create success id=%s name=%s principals=%d targets=%d", item.ID, item.Name, len(item.Principals), len(item.Targets))
WriteJSON(w, http.StatusCreated, buildSSHPrincipalGrantSummary(item))
}
@@ -256,11 +256,11 @@ func (api *API) UpdateSSHPrincipalGrant(w http.ResponseWriter, r *http.Request,
item.Targets = targets
item, err = api.store(r).UpdateSSHPrincipalGrant(item)
if err != nil {
api.Logger.Write(logIDSSHCA, util.LOG_WARN, "grant update failed id=%s err=%v", params["id"], err)
api.Logger.Write(logIDSSHCA, codit_logger.LOG_WARN, "grant update failed id=%s err=%v", params["id"], err)
WriteJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
return
}
api.Logger.Write(logIDSSHCA, util.LOG_INFO, "grant update success id=%s name=%s principals=%d targets=%d", item.ID, item.Name, len(item.Principals), len(item.Targets))
api.Logger.Write(logIDSSHCA, codit_logger.LOG_INFO, "grant update success id=%s name=%s principals=%d targets=%d", item.ID, item.Name, len(item.Principals), len(item.Targets))
WriteJSON(w, http.StatusOK, buildSSHPrincipalGrantSummary(item))
}
@@ -271,11 +271,11 @@ func (api *API) DeleteSSHPrincipalGrant(w http.ResponseWriter, r *http.Request,
}
err = api.store(r).DeleteSSHPrincipalGrant(params["id"])
if err != nil {
api.Logger.Write(logIDSSHCA, util.LOG_WARN, "grant delete failed id=%s err=%v", params["id"], err)
api.Logger.Write(logIDSSHCA, codit_logger.LOG_WARN, "grant delete failed id=%s err=%v", params["id"], err)
WriteJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
}
api.Logger.Write(logIDSSHCA, util.LOG_INFO, "grant delete success id=%s", params["id"])
api.Logger.Write(logIDSSHCA, codit_logger.LOG_INFO, "grant delete success id=%s", params["id"])
WriteJSON(w, http.StatusOK, map[string]string{"status": "deleted"})
}
+12 -12
View File
@@ -1,5 +1,6 @@
package handlers
import codit_logger "codit/logger"
import "crypto/ed25519"
import "crypto/ecdsa"
import "crypto/elliptic"
@@ -22,7 +23,6 @@ import "golang.org/x/crypto/ssh"
import "codit/internal/middleware"
import "codit/internal/models"
import "codit/internal/util"
type sshUserCACreateRequest struct {
Name string `json:"name"`
@@ -207,12 +207,12 @@ func (api *API) CreateSSHUserCA(w http.ResponseWriter, r *http.Request, _ map[st
}
item, err = api.store(r).CreateSSHUserCA(item)
if err != nil {
api.Logger.Write(logIDSSHCA, util.LOG_WARN, "create failed name=%s err=%v", req.Name, err)
api.Logger.Write(logIDSSHCA, codit_logger.LOG_WARN, "create failed name=%s err=%v", req.Name, err)
WriteJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
return
}
summary = buildSSHUserCASummary(item)
api.Logger.Write(logIDSSHCA, util.LOG_INFO, "create success id=%s name=%s algorithm=%s", summary.ID, summary.Name, summary.Algorithm)
api.Logger.Write(logIDSSHCA, codit_logger.LOG_INFO, "create success id=%s name=%s algorithm=%s", summary.ID, summary.Name, summary.Algorithm)
WriteJSON(w, http.StatusCreated, summary)
}
@@ -254,11 +254,11 @@ func (api *API) UpdateSSHUserCA(w http.ResponseWriter, r *http.Request, params m
item.MaxUserValidSeconds = maxUserValidSeconds
item, err = api.store(r).UpdateSSHUserCA(item)
if err != nil {
api.Logger.Write(logIDSSHCA, util.LOG_WARN, "update failed id=%s err=%v", params["id"], err)
api.Logger.Write(logIDSSHCA, codit_logger.LOG_WARN, "update failed id=%s err=%v", params["id"], err)
WriteJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
return
}
api.Logger.Write(logIDSSHCA, util.LOG_INFO, "update success id=%s name=%s enabled=%t", item.ID, item.Name, item.Enabled)
api.Logger.Write(logIDSSHCA, codit_logger.LOG_INFO, "update success id=%s name=%s enabled=%t", item.ID, item.Name, item.Enabled)
WriteJSON(w, http.StatusOK, buildSSHUserCASummary(item))
}
@@ -269,11 +269,11 @@ func (api *API) DeleteSSHUserCA(w http.ResponseWriter, r *http.Request, params m
}
err = api.store(r).DeleteSSHUserCA(params["id"])
if err != nil {
api.Logger.Write(logIDSSHCA, util.LOG_WARN, "delete failed id=%s err=%v", params["id"], err)
api.Logger.Write(logIDSSHCA, codit_logger.LOG_WARN, "delete failed id=%s err=%v", params["id"], err)
WriteJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
}
api.Logger.Write(logIDSSHCA, util.LOG_INFO, "delete success id=%s", params["id"])
api.Logger.Write(logIDSSHCA, codit_logger.LOG_INFO, "delete success id=%s", params["id"])
WriteJSON(w, http.StatusOK, map[string]string{"status": "deleted"})
}
@@ -385,11 +385,11 @@ func (api *API) SignSSHUserKey(w http.ResponseWriter, r *http.Request, params ma
}
err = api.recordSSHUserCAIssuance(r, item, user, "admin", sourcePublicKey, sourcePublicKeyFingerprint, nil, nil, response)
if err != nil {
api.Logger.Write(logIDSSHCA, util.LOG_WARN, "audit write failed ca_id=%s serial=%d err=%v", item.ID, response.Serial, err)
api.Logger.Write(logIDSSHCA, codit_logger.LOG_WARN, "audit write failed ca_id=%s serial=%d err=%v", item.ID, response.Serial, err)
WriteJSON(w, http.StatusInternalServerError, map[string]string{"error": "failed to record issuance audit"})
return
}
api.Logger.Write(logIDSSHCA, util.LOG_INFO, "sign success ca_id=%s serial=%d key_id=%s principals=%s", item.ID, response.Serial, response.KeyID, strings.Join(principals, ","))
api.Logger.Write(logIDSSHCA, codit_logger.LOG_INFO, "sign success ca_id=%s serial=%d key_id=%s principals=%s", item.ID, response.Serial, response.KeyID, strings.Join(principals, ","))
WriteJSON(w, http.StatusOK, response)
}
@@ -720,18 +720,18 @@ func (api *API) SignSSHUserKeyForSelf(w http.ResponseWriter, r *http.Request, pa
for grantID = range selectedGrantIDs {
err = api.store(r).MarkSSHPrincipalGrantUsed(grantID)
if err != nil {
api.Logger.Write(logIDSSHCA, util.LOG_WARN, "self-sign grant consume failed ca_id=%s user=%s grant_id=%s err=%v", item.ID, user.Username, grantID, err)
api.Logger.Write(logIDSSHCA, codit_logger.LOG_WARN, "self-sign grant consume failed ca_id=%s user=%s grant_id=%s err=%v", item.ID, user.Username, grantID, err)
WriteJSON(w, http.StatusBadRequest, map[string]string{"error": "selected principal grant is not active"})
return
}
}
err = api.recordSSHUserCAIssuance(r, item, user, "self", sourcePublicKey, sourcePublicKeyFingerprint, selectedIDs, selectedGrantNames, response)
if err != nil {
api.Logger.Write(logIDSSHCA, util.LOG_WARN, "self-sign audit write failed ca_id=%s user=%s serial=%d err=%v", item.ID, user.Username, response.Serial, err)
api.Logger.Write(logIDSSHCA, codit_logger.LOG_WARN, "self-sign audit write failed ca_id=%s user=%s serial=%d err=%v", item.ID, user.Username, response.Serial, err)
WriteJSON(w, http.StatusInternalServerError, map[string]string{"error": "failed to record issuance audit"})
return
}
api.Logger.Write(logIDSSHCA, util.LOG_INFO, "self-sign success ca_id=%s user=%s serial=%d key_id=%s", item.ID, user.Username, response.Serial, response.KeyID)
api.Logger.Write(logIDSSHCA, codit_logger.LOG_INFO, "self-sign success ca_id=%s user=%s serial=%d key_id=%s", item.ID, user.Username, response.Serial, response.KeyID)
WriteJSON(w, http.StatusOK, response)
}
+3 -3
View File
@@ -1,5 +1,6 @@
package middleware
import codit_logger "codit/logger"
import "bufio"
import "fmt"
import "io"
@@ -8,7 +9,6 @@ import "net/http"
import "time"
import "codit/internal/models"
import "codit/internal/util"
type statusRecorder struct {
http.ResponseWriter
@@ -75,7 +75,7 @@ func (r *statusRecorder) ReadFrom(src io.Reader) (int64, error) {
return io.Copy(r.ResponseWriter, src)
}
func AccessLog(logger *util.Logger, next http.Handler) http.Handler {
func AccessLog(logger codit_logger.Logger, next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var recorder *statusRecorder
var start time.Time
@@ -104,7 +104,7 @@ func AccessLog(logger *util.Logger, next http.Handler) http.Handler {
if !ok || authReason == "" {
authReason = "-"
}
logger.Write(logIDAPI, util.LOG_INFO, "method=%s path=%s remote=%s user=%s mtls_cn=%q mtls_issuer_cn=%q mtls_serial=%q mtls_user_id=%q mtls_username=%q mtls_profile_id=%q mtls_permissions=%q mtls_scope=%q status=%d dur_ms=%d auth_reason=%s",
logger.Write(logIDAPI, codit_logger.LOG_INFO, "method=%s path=%s remote=%s user=%s mtls_cn=%q mtls_issuer_cn=%q mtls_serial=%q mtls_user_id=%q mtls_username=%q mtls_profile_id=%q mtls_permissions=%q mtls_scope=%q status=%d dur_ms=%d auth_reason=%s",
r.Method, r.URL.Path, r.RemoteAddr, userLabel, mtlsInfo.SubjectCN, mtlsInfo.IssuerCN, mtlsInfo.Serial, mtlsInfo.UserID, mtlsInfo.Username, mtlsInfo.ProfileID, mtlsInfo.Permissions, mtlsInfo.Scope, recorder.status, duration.Milliseconds(), authReason)
})
}
+25 -3
View File
@@ -1,24 +1,46 @@
package middleware
import codit_logger "codit/logger"
import "bytes"
import "context"
import "fmt"
import "net/http"
import "net/http/httptest"
import "strings"
import "testing"
import "codit/internal/models"
import "codit/internal/util"
type testLogger struct {
buf *bytes.Buffer
}
func (l *testLogger) Write(id string, level codit_logger.LogLevel, fmtstr string, args ...interface{}) {
var line string
line = fmt.Sprintf(fmtstr, args...)
l.buf.WriteString(line)
}
func (l *testLogger) WriteWithCallDepth(id string, level codit_logger.LogLevel, call_depth int, fmtstr string, args ...interface{}) {
l.Write(id, level, fmtstr, args...)
}
func (l *testLogger) Rotate() {
}
func (l *testLogger) Close() {
}
func TestAccessLogWritesRecord(t *testing.T) {
var buf bytes.Buffer
var logger *util.Logger
var logger codit_logger.Logger
var handler http.Handler
var req *http.Request
var recorder *httptest.ResponseRecorder
var user models.User
var ctx context.Context
logger = util.NewLogger("test", &buf, util.LOG_ALL)
logger = &testLogger{buf: &buf}
defer logger.Close()
handler = AccessLog(logger, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusCreated)
+1 -1
View File
@@ -15,7 +15,7 @@ func WithCors(next http.Handler) http.Handler {
h.Add("Access-Control-Allow-Methods", "*")
h.Add("Access-Control-Allow-Origin", "*")
if r.Method == "OPTIONS" {
if r.Method == http.MethodOptions {
w.WriteHeader(http.StatusOK)
return
}
+6 -6
View File
@@ -1,20 +1,20 @@
package rpm
import codit_logger "codit/logger"
import "codit/internal/middleware"
import "codit/internal/util"
import "net/http"
type HTTPServer struct {
handler http.Handler
auth AuthFunc
logger *util.Logger
logger codit_logger.Logger
}
type AuthFunc func(username, password string) (bool, error)
const logIDRPM string = "rpm"
func NewHTTPServer(baseDir string, auth AuthFunc, logger *util.Logger) *HTTPServer {
func NewHTTPServer(baseDir string, auth AuthFunc, logger codit_logger.Logger) *HTTPServer {
var server HTTPServer
server = HTTPServer{
handler: http.FileServer(http.Dir(baseDir)),
@@ -47,7 +47,7 @@ func (s *HTTPServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if !hasLogInfo {
logInfo = RequestLogInfo{OrgPath: "-", ProjectID: "-", ProjectSlug: "-", RepoID: "-", RepoName: "-"}
}
s.logger.Write(logIDRPM, util.LOG_INFO, "method=%s path=%s orgpath=%s project_id=%s project_slug=%s repo_id=%s repo_name=%s remote=%s user=%s mtls_cn=%q mtls_issuer_cn=%q mtls_serial=%q mtls_user_id=%q mtls_username=%q mtls_profile_id=%q mtls_permissions=%q mtls_scope=%q status=%d", r.Method, r.URL.Path, logInfo.OrgPath, logInfo.ProjectID, logInfo.ProjectSlug, logInfo.RepoID, logInfo.RepoName, r.RemoteAddr, userLabel, mtlsInfo.SubjectCN, mtlsInfo.IssuerCN, mtlsInfo.Serial, mtlsInfo.UserID, mtlsInfo.Username, mtlsInfo.ProfileID, mtlsInfo.Permissions, mtlsInfo.Scope, recorder.status)
s.logger.Write(logIDRPM, codit_logger.LOG_INFO, "method=%s path=%s orgpath=%s project_id=%s project_slug=%s repo_id=%s repo_name=%s remote=%s user=%s mtls_cn=%q mtls_issuer_cn=%q mtls_serial=%q mtls_user_id=%q mtls_username=%q mtls_profile_id=%q mtls_permissions=%q mtls_scope=%q status=%d", r.Method, r.URL.Path, logInfo.OrgPath, logInfo.ProjectID, logInfo.ProjectSlug, logInfo.RepoID, logInfo.RepoName, r.RemoteAddr, userLabel, mtlsInfo.SubjectCN, mtlsInfo.IssuerCN, mtlsInfo.Serial, mtlsInfo.UserID, mtlsInfo.Username, mtlsInfo.ProfileID, mtlsInfo.Permissions, mtlsInfo.Scope, recorder.status)
return
}
allowed, err = s.auth(username, password)
@@ -59,7 +59,7 @@ func (s *HTTPServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if !hasLogInfo {
logInfo = RequestLogInfo{OrgPath: "-", ProjectID: "-", ProjectSlug: "-", RepoID: "-", RepoName: "-"}
}
s.logger.Write(logIDRPM, util.LOG_INFO, "method=%s path=%s orgpath=%s project_id=%s project_slug=%s repo_id=%s repo_name=%s remote=%s user=%s mtls_cn=%q mtls_issuer_cn=%q mtls_serial=%q mtls_user_id=%q mtls_username=%q mtls_profile_id=%q mtls_permissions=%q mtls_scope=%q status=%d", r.Method, r.URL.Path, logInfo.OrgPath, logInfo.ProjectID, logInfo.ProjectSlug, logInfo.RepoID, logInfo.RepoName, r.RemoteAddr, userLabel, mtlsInfo.SubjectCN, mtlsInfo.IssuerCN, mtlsInfo.Serial, mtlsInfo.UserID, mtlsInfo.Username, mtlsInfo.ProfileID, mtlsInfo.Permissions, mtlsInfo.Scope, recorder.status)
s.logger.Write(logIDRPM, codit_logger.LOG_INFO, "method=%s path=%s orgpath=%s project_id=%s project_slug=%s repo_id=%s repo_name=%s remote=%s user=%s mtls_cn=%q mtls_issuer_cn=%q mtls_serial=%q mtls_user_id=%q mtls_username=%q mtls_profile_id=%q mtls_permissions=%q mtls_scope=%q status=%d", r.Method, r.URL.Path, logInfo.OrgPath, logInfo.ProjectID, logInfo.ProjectSlug, logInfo.RepoID, logInfo.RepoName, r.RemoteAddr, userLabel, mtlsInfo.SubjectCN, mtlsInfo.IssuerCN, mtlsInfo.Serial, mtlsInfo.UserID, mtlsInfo.Username, mtlsInfo.ProfileID, mtlsInfo.Permissions, mtlsInfo.Scope, recorder.status)
return
}
userLabel = username
@@ -69,7 +69,7 @@ func (s *HTTPServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if !hasLogInfo {
logInfo = RequestLogInfo{OrgPath: "-", ProjectID: "-", ProjectSlug: "-", RepoID: "-", RepoName: "-"}
}
s.logger.Write(logIDRPM, util.LOG_INFO, "method=%s path=%s orgpath=%s project_id=%s project_slug=%s repo_id=%s repo_name=%s remote=%s user=%s mtls_cn=%q mtls_issuer_cn=%q mtls_serial=%q mtls_user_id=%q mtls_username=%q mtls_profile_id=%q mtls_permissions=%q mtls_scope=%q status=%d", r.Method, r.URL.Path, logInfo.OrgPath, logInfo.ProjectID, logInfo.ProjectSlug, logInfo.RepoID, logInfo.RepoName, r.RemoteAddr, userLabel, mtlsInfo.SubjectCN, mtlsInfo.IssuerCN, mtlsInfo.Serial, mtlsInfo.UserID, mtlsInfo.Username, mtlsInfo.ProfileID, mtlsInfo.Permissions, mtlsInfo.Scope, recorder.status)
s.logger.Write(logIDRPM, codit_logger.LOG_INFO, "method=%s path=%s orgpath=%s project_id=%s project_slug=%s repo_id=%s repo_name=%s remote=%s user=%s mtls_cn=%q mtls_issuer_cn=%q mtls_serial=%q mtls_user_id=%q mtls_username=%q mtls_profile_id=%q mtls_permissions=%q mtls_scope=%q status=%d", r.Method, r.URL.Path, logInfo.OrgPath, logInfo.ProjectID, logInfo.ProjectSlug, logInfo.RepoID, logInfo.RepoName, r.RemoteAddr, userLabel, mtlsInfo.SubjectCN, mtlsInfo.IssuerCN, mtlsInfo.Serial, mtlsInfo.UserID, mtlsInfo.Username, mtlsInfo.ProfileID, mtlsInfo.Permissions, mtlsInfo.Scope, recorder.status)
}
type statusRecorder struct {
+19 -6
View File
@@ -1,22 +1,35 @@
package rpm
import "bytes"
import codit_logger "codit/logger"
import "net/http"
import "net/http/httptest"
import "os"
import "path/filepath"
import "testing"
import "codit/internal/util"
type testLogger struct {
}
func (l *testLogger) Write(id string, level codit_logger.LogLevel, fmtstr string, args ...interface{}) {
}
func (l *testLogger) WriteWithCallDepth(id string, level codit_logger.LogLevel, call_depth int, fmtstr string, args ...interface{}) {
}
func (l *testLogger) Rotate() {
}
func (l *testLogger) Close() {
}
func TestHTTPServerUnauthorizedWithoutBasicAuth(t *testing.T) {
var dir string
var logger *util.Logger
var logger codit_logger.Logger
var server *HTTPServer
var req *http.Request
var recorder *httptest.ResponseRecorder
dir = t.TempDir()
logger = util.NewLogger("test", &bytes.Buffer{}, util.LOG_ALL)
logger = &testLogger{}
defer logger.Close()
server = NewHTTPServer(dir, func(username, password string) (bool, error) {
return true, nil
@@ -31,7 +44,7 @@ func TestHTTPServerUnauthorizedWithoutBasicAuth(t *testing.T) {
func TestHTTPServerAuthorized(t *testing.T) {
var dir string
var logger *util.Logger
var logger codit_logger.Logger
var server *HTTPServer
var req *http.Request
var recorder *httptest.ResponseRecorder
@@ -39,7 +52,7 @@ func TestHTTPServerAuthorized(t *testing.T) {
dir = t.TempDir()
path = filepath.Join(dir, "a.txt")
_ = os.WriteFile(path, []byte("ok"), 0o644)
logger = util.NewLogger("test", &bytes.Buffer{}, util.LOG_ALL)
logger = &testLogger{}
defer logger.Close()
server = NewHTTPServer(dir, func(username, password string) (bool, error) {
return username == "u" && password == "p", nil
+29 -29
View File
@@ -1,5 +1,6 @@
package rpm
import codit_logger "codit/logger"
import "compress/gzip"
import "context"
import "crypto/md5"
@@ -26,13 +27,12 @@ import "time"
import "codit/internal/db"
import "codit/internal/models"
import "codit/internal/util"
const logIDRPMMirror string = "rpm-mirror"
type MirrorManager struct {
store *db.Store
logger *util.Logger
logger codit_logger.Logger
meta *MetaManager
stopCh chan struct{}
cancelMu sync.Mutex
@@ -93,7 +93,7 @@ type mirrorHTTPConfig struct {
DefaultServer string
}
func NewMirrorManager(store *db.Store, logger *util.Logger, meta *MetaManager) *MirrorManager {
func NewMirrorManager(store *db.Store, logger codit_logger.Logger, meta *MetaManager) *MirrorManager {
var m *MirrorManager
m = &MirrorManager{
store: store,
@@ -131,7 +131,7 @@ func (m *MirrorManager) Start() {
}
err = m.store.ResetRunningRPMMirrorTasks()
if err != nil && m.logger != nil {
m.logger.Write(logIDRPMMirror, util.LOG_ERROR, "reset running tasks failed err=%v", err)
m.logger.Write(logIDRPMMirror, codit_logger.LOG_ERROR, "reset running tasks failed err=%v", err)
}
tasks, err = m.store.ListRPMMirrorPaths()
if err == nil {
@@ -175,7 +175,7 @@ func (m *MirrorManager) runDue() {
tasks, err = m.store.ListDueRPMMirrorTasks(now, 8)
if err != nil {
if m.logger != nil {
m.logger.Write(logIDRPMMirror, util.LOG_ERROR, "list due tasks failed err=%v", err)
m.logger.Write(logIDRPMMirror, codit_logger.LOG_ERROR, "list due tasks failed err=%v", err)
}
return
}
@@ -183,7 +183,7 @@ func (m *MirrorManager) runDue() {
runID, started, err = m.store.TryStartRPMMirrorRun(tasks[i].RepoID, tasks[i].MirrorPath, now)
if err != nil {
if m.logger != nil {
m.logger.Write(logIDRPMMirror, util.LOG_ERROR, "try start failed repo=%s path=%s err=%v", tasks[i].RepoID, tasks[i].MirrorPath, err)
m.logger.Write(logIDRPMMirror, codit_logger.LOG_ERROR, "try start failed repo=%s path=%s err=%v", tasks[i].RepoID, tasks[i].MirrorPath, err)
}
continue
}
@@ -229,20 +229,20 @@ func (m *MirrorManager) syncOne(task models.RPMMirrorTask, runID string) {
cfg, err = buildMirrorHTTPConfig(task)
if err != nil {
if m.logger != nil {
m.logger.Write(logIDRPMMirror, util.LOG_ERROR, "sync failed repo=%s path=%s step=start err=%v", task.RepoID, task.MirrorPath, err)
m.logger.Write(logIDRPMMirror, codit_logger.LOG_ERROR, "sync failed repo=%s path=%s step=start err=%v", task.RepoID, task.MirrorPath, err)
}
m.finishTaskRun(task, runID, false, "start", 0, 0, 0, 0, "", err.Error())
return
}
client = buildMirrorHTTPClient(cfg)
if m.logger != nil {
m.logger.Write(logIDRPMMirror, util.LOG_INFO, "sync start repo=%s path=%s remote=%s", task.RepoID, task.MirrorPath, task.RemoteURL)
m.logger.Write(logIDRPMMirror, codit_logger.LOG_INFO, "sync start repo=%s path=%s remote=%s", task.RepoID, task.MirrorPath, task.RemoteURL)
}
_ = m.store.UpdateRPMMirrorTaskProgress(task.RepoID, task.MirrorPath, "fetch_repodata", 0, 0, 0, 0)
repomdData, err = mirrorFetch(syncCtx, client, cfg, "repodata/repomd.xml")
if err != nil {
if m.logger != nil {
m.logger.Write(logIDRPMMirror, util.LOG_ERROR, "sync failed repo=%s path=%s step=fetch_repodata err=%v", task.RepoID, task.MirrorPath, err)
m.logger.Write(logIDRPMMirror, codit_logger.LOG_ERROR, "sync failed repo=%s path=%s step=fetch_repodata err=%v", task.RepoID, task.MirrorPath, err)
}
m.finishTaskRun(task, runID, false, "fetch_repodata", 0, 0, 0, 0, "", err.Error())
return
@@ -253,7 +253,7 @@ func (m *MirrorManager) syncOne(task models.RPMMirrorTask, runID string) {
ensureRepodata(task, localRoot, m.meta, m.logger)
}
if m.logger != nil {
m.logger.Write(logIDRPMMirror, util.LOG_INFO, "sync done repo=%s path=%s status=no_change revision=%s", task.RepoID, task.MirrorPath, revision)
m.logger.Write(logIDRPMMirror, codit_logger.LOG_INFO, "sync done repo=%s path=%s status=no_change revision=%s", task.RepoID, task.MirrorPath, revision)
}
m.finishTaskRun(task, runID, true, "no_change", 0, 0, 0, 0, revision, "")
return
@@ -261,7 +261,7 @@ func (m *MirrorManager) syncOne(task models.RPMMirrorTask, runID string) {
primaryHref, err = parseRepomdPrimaryHref(repomdData)
if err != nil {
if m.logger != nil {
m.logger.Write(logIDRPMMirror, util.LOG_ERROR, "sync failed repo=%s path=%s step=parse_repodata err=%v", task.RepoID, task.MirrorPath, err)
m.logger.Write(logIDRPMMirror, codit_logger.LOG_ERROR, "sync failed repo=%s path=%s step=parse_repodata err=%v", task.RepoID, task.MirrorPath, err)
}
m.finishTaskRun(task, runID, false, "fetch_repodata", 0, 0, 0, 0, "", err.Error())
return
@@ -270,7 +270,7 @@ func (m *MirrorManager) syncOne(task models.RPMMirrorTask, runID string) {
primaryData, err = mirrorFetch(syncCtx, client, cfg, primaryHref)
if err != nil {
if m.logger != nil {
m.logger.Write(logIDRPMMirror, util.LOG_ERROR, "sync failed repo=%s path=%s step=fetch_primary err=%v", task.RepoID, task.MirrorPath, err)
m.logger.Write(logIDRPMMirror, codit_logger.LOG_ERROR, "sync failed repo=%s path=%s step=fetch_primary err=%v", task.RepoID, task.MirrorPath, err)
}
m.finishTaskRun(task, runID, false, "fetch_primary", 0, 0, 0, 0, "", err.Error())
return
@@ -279,7 +279,7 @@ func (m *MirrorManager) syncOne(task models.RPMMirrorTask, runID string) {
primaryData, err = gunzipBytes(primaryData)
if err != nil {
if m.logger != nil {
m.logger.Write(logIDRPMMirror, util.LOG_ERROR, "sync failed repo=%s path=%s step=decode_primary err=%v", task.RepoID, task.MirrorPath, err)
m.logger.Write(logIDRPMMirror, codit_logger.LOG_ERROR, "sync failed repo=%s path=%s step=decode_primary err=%v", task.RepoID, task.MirrorPath, err)
}
m.finishTaskRun(task, runID, false, "fetch_primary", 0, 0, 0, 0, "", err.Error())
return
@@ -288,15 +288,15 @@ func (m *MirrorManager) syncOne(task models.RPMMirrorTask, runID string) {
expected, duplicateCount, err = parsePrimaryPackages(primaryData)
if err != nil {
if m.logger != nil {
m.logger.Write(logIDRPMMirror, util.LOG_ERROR, "sync failed repo=%s path=%s step=parse_primary err=%v", task.RepoID, task.MirrorPath, err)
m.logger.Write(logIDRPMMirror, codit_logger.LOG_ERROR, "sync failed repo=%s path=%s step=parse_primary err=%v", task.RepoID, task.MirrorPath, err)
}
m.finishTaskRun(task, runID, false, "fetch_primary", 0, 0, 0, 0, "", err.Error())
return
}
if m.logger != nil {
m.logger.Write(logIDRPMMirror, util.LOG_INFO, "primary parsed repo=%s path=%s primary_href=%s packages=%d", task.RepoID, task.MirrorPath, primaryHref, len(expected))
m.logger.Write(logIDRPMMirror, codit_logger.LOG_INFO, "primary parsed repo=%s path=%s primary_href=%s packages=%d", task.RepoID, task.MirrorPath, primaryHref, len(expected))
if duplicateCount > 0 {
m.logger.Write(logIDRPMMirror, util.LOG_WARN, "primary has duplicate package paths repo=%s path=%s primary_href=%s duplicates=%d", task.RepoID, task.MirrorPath, primaryHref, duplicateCount)
m.logger.Write(logIDRPMMirror, codit_logger.LOG_WARN, "primary has duplicate package paths repo=%s path=%s primary_href=%s duplicates=%d", task.RepoID, task.MirrorPath, primaryHref, duplicateCount)
}
}
total, done, failed, deleted, changed, err = m.applyMirror(syncCtx, task, localRoot, client, cfg, expected)
@@ -304,9 +304,9 @@ func (m *MirrorManager) syncOne(task models.RPMMirrorTask, runID string) {
canceled = errors.Is(err, context.Canceled)
if m.logger != nil {
if canceled {
m.logger.Write(logIDRPMMirror, util.LOG_WARN, "sync canceled repo=%s path=%s step=apply total=%d done=%d failed=%d deleted=%d err=%v", task.RepoID, task.MirrorPath, total, done, failed, deleted, err)
m.logger.Write(logIDRPMMirror, codit_logger.LOG_WARN, "sync canceled repo=%s path=%s step=apply total=%d done=%d failed=%d deleted=%d err=%v", task.RepoID, task.MirrorPath, total, done, failed, deleted, err)
} else {
m.logger.Write(logIDRPMMirror, util.LOG_ERROR, "sync failed repo=%s path=%s step=apply total=%d done=%d failed=%d deleted=%d err=%v", task.RepoID, task.MirrorPath, total, done, failed, deleted, err)
m.logger.Write(logIDRPMMirror, codit_logger.LOG_ERROR, "sync failed repo=%s path=%s step=apply total=%d done=%d failed=%d deleted=%d err=%v", task.RepoID, task.MirrorPath, total, done, failed, deleted, err)
}
}
if canceled {
@@ -318,14 +318,14 @@ func (m *MirrorManager) syncOne(task models.RPMMirrorTask, runID string) {
}
if m.meta != nil && changed > 0 {
if m.logger != nil {
m.logger.Write(logIDRPMMirror, util.LOG_INFO, "repodata schedule repo=%s path=%s reason=sync_changed changed=%d", task.RepoID, task.MirrorPath, changed)
m.logger.Write(logIDRPMMirror, codit_logger.LOG_INFO, "repodata schedule repo=%s path=%s reason=sync_changed changed=%d", task.RepoID, task.MirrorPath, changed)
}
m.meta.Schedule(localRoot)
}
m.finishTaskRun(task, runID, true, "done", total, done, failed, deleted, revision, "")
_ = m.store.CleanupRPMMirrorRunsRetention(task.RepoID, task.MirrorPath, 200, 30)
if m.logger != nil {
m.logger.Write(logIDRPMMirror, util.LOG_INFO, "sync done repo=%s path=%s status=success total=%d done=%d failed=%d deleted=%d revision=%s", task.RepoID, task.MirrorPath, total, done, failed, deleted, revision)
m.logger.Write(logIDRPMMirror, codit_logger.LOG_INFO, "sync done repo=%s path=%s status=success total=%d done=%d failed=%d deleted=%d revision=%s", task.RepoID, task.MirrorPath, total, done, failed, deleted, revision)
}
}
@@ -336,7 +336,7 @@ func (m *MirrorManager) finishTaskRun(task models.RPMMirrorTask, runID string, s
finishedAt = time.Now().UTC().Unix()
err = m.store.FinishRPMMirrorTaskRun(task.RepoID, task.MirrorPath, runID, success, finishedAt, step, total, done, failed, deleted, revision, errMsg)
if err != nil && m.logger != nil {
m.logger.Write(logIDRPMMirror, util.LOG_ERROR, "finish sync state failed repo=%s path=%s run=%s err=%v", task.RepoID, task.MirrorPath, runID, err)
m.logger.Write(logIDRPMMirror, codit_logger.LOG_ERROR, "finish sync state failed repo=%s path=%s run=%s err=%v", task.RepoID, task.MirrorPath, runID, err)
}
}
@@ -369,7 +369,7 @@ func (m *MirrorManager) applyMirror(ctx context.Context, task models.RPMMirrorTa
continue
}
if m.logger != nil {
m.logger.Write(logIDRPMMirror, util.LOG_DEBUG, "delete local stale repo=%s path=%s file=%s", task.RepoID, task.MirrorPath, path)
m.logger.Write(logIDRPMMirror, codit_logger.LOG_DEBUG, "delete local stale repo=%s path=%s file=%s", task.RepoID, task.MirrorPath, path)
}
err = os.Remove(filepath.Join(localRoot, filepath.FromSlash(path)))
if err == nil || os.IsNotExist(err) {
@@ -377,7 +377,7 @@ func (m *MirrorManager) applyMirror(ctx context.Context, task models.RPMMirrorTa
changed = changed + 1
} else {
if m.logger != nil {
m.logger.Write(logIDRPMMirror, util.LOG_WARN, "delete local stale failed repo=%s path=%s file=%s err=%v", task.RepoID, task.MirrorPath, path, err)
m.logger.Write(logIDRPMMirror, codit_logger.LOG_WARN, "delete local stale failed repo=%s path=%s file=%s err=%v", task.RepoID, task.MirrorPath, path, err)
}
}
}
@@ -398,24 +398,24 @@ func (m *MirrorManager) applyMirror(ctx context.Context, task models.RPMMirrorTa
}
if needDownload {
if m.logger != nil {
m.logger.Write(logIDRPMMirror, util.LOG_DEBUG, "download start repo=%s path=%s file=%s checksum_type=%s checksum=%s", task.RepoID, task.MirrorPath, path, checksum.Algo, checksum.Value)
m.logger.Write(logIDRPMMirror, codit_logger.LOG_DEBUG, "download start repo=%s path=%s file=%s checksum_type=%s checksum=%s", task.RepoID, task.MirrorPath, path, checksum.Algo, checksum.Value)
}
err = mirrorDownload(ctx, client, cfg, path, fullPath, checksum.Algo, checksum.Value)
if err != nil {
failed = failed + 1
if m.logger != nil {
m.logger.Write(logIDRPMMirror, util.LOG_WARN, "download failed repo=%s path=%s file=%s err=%v", task.RepoID, task.MirrorPath, path, err)
m.logger.Write(logIDRPMMirror, codit_logger.LOG_WARN, "download failed repo=%s path=%s file=%s err=%v", task.RepoID, task.MirrorPath, path, err)
}
_ = m.store.UpdateRPMMirrorTaskProgress(task.RepoID, task.MirrorPath, "apply", total, done, failed, deleted)
continue
}
changed = changed + 1
if m.logger != nil {
m.logger.Write(logIDRPMMirror, util.LOG_DEBUG, "download done repo=%s path=%s file=%s", task.RepoID, task.MirrorPath, path)
m.logger.Write(logIDRPMMirror, codit_logger.LOG_DEBUG, "download done repo=%s path=%s file=%s", task.RepoID, task.MirrorPath, path)
}
} else {
if m.logger != nil {
m.logger.Write(logIDRPMMirror, util.LOG_DEBUG, "download skip repo=%s path=%s file=%s reason=up-to-date", task.RepoID, task.MirrorPath, path)
m.logger.Write(logIDRPMMirror, codit_logger.LOG_DEBUG, "download skip repo=%s path=%s file=%s reason=up-to-date", task.RepoID, task.MirrorPath, path)
}
}
done = done + 1
@@ -853,7 +853,7 @@ func mirrorTaskKey(repoID string, path string) string {
return repoID + "\x00" + path
}
func ensureRepodata(task models.RPMMirrorTask, localRoot string, meta *MetaManager, logger *util.Logger) {
func ensureRepodata(task models.RPMMirrorTask, localRoot string, meta *MetaManager, logger codit_logger.Logger) {
var repomdPath string
var statErr error
repomdPath = filepath.Join(localRoot, "repodata", "repomd.xml")
@@ -862,7 +862,7 @@ func ensureRepodata(task models.RPMMirrorTask, localRoot string, meta *MetaManag
return
}
if logger != nil {
logger.Write(logIDRPMMirror, util.LOG_INFO, "repodata schedule repo=%s path=%s reason=missing repomd=%s", task.RepoID, task.MirrorPath, repomdPath)
logger.Write(logIDRPMMirror, codit_logger.LOG_INFO, "repodata schedule repo=%s path=%s reason=missing repomd=%s", task.RepoID, task.MirrorPath, repomdPath)
}
meta.Schedule(localRoot)
}
+35
View File
@@ -0,0 +1,35 @@
package codit
import "io"
import codit_logger "codit/logger"
// this public file is to workaround the import-cycle issue.
// codit/logger can't import from the root package.
type LogLevel = codit_logger.LogLevel
type LogMask = codit_logger.LogMask
type Logger = codit_logger.Logger
type AsyncLogger = codit_logger.AsyncLogger
const LOG_DEBUG LogLevel = codit_logger.LOG_DEBUG
const LOG_INFO LogLevel = codit_logger.LOG_INFO
const LOG_WARN LogLevel = codit_logger.LOG_WARN
const LOG_ERROR LogLevel = codit_logger.LOG_ERROR
const LOG_ALL LogMask = codit_logger.LOG_ALL
const LOG_NONE LogMask = codit_logger.LOG_NONE
func NewAsyncLogger(id string, w io.Writer, mask LogMask) *AsyncLogger {
return codit_logger.NewAsyncLogger(id, w, mask)
}
func NewAsyncLoggerToFile(id string, file_name string, max_size int64, rotate int, mask LogMask) (*AsyncLogger, error) {
return codit_logger.NewAsyncLoggerToFile(id, file_name, max_size, rotate, mask)
}
func LogStrsToMask(str []string) LogMask {
return codit_logger.LogStrsToMask(str)
}
@@ -1,4 +1,4 @@
package util
package logger
import "fmt"
import "io"
@@ -11,8 +11,8 @@ import "sync/atomic"
import "syscall"
import "time"
type LogLevel int
type LogMask int
type LogLevel = int
type LogMask = int
const (
LOG_DEBUG LogLevel = 1 << iota
@@ -24,12 +24,19 @@ const (
const LOG_ALL LogMask = LogMask(LOG_DEBUG | LOG_INFO | LOG_WARN | LOG_ERROR)
const LOG_NONE LogMask = LogMask(0)
type Logger interface {
Write(id string, level LogLevel, fmtstr string, args ...interface{})
WriteWithCallDepth(id string, level LogLevel, call_depth int, fmtstr string, args ...interface{})
Rotate()
Close()
}
type logger_msg_t struct {
code int
data string
}
type Logger struct {
type AsyncLogger struct {
id string
out io.Writer
mask LogMask
@@ -63,8 +70,8 @@ func _is_ansi_tty(fd uintptr) bool {
}
func NewLogger(id string, w io.Writer, mask LogMask) *Logger {
var l *Logger
func NewAsyncLogger(id string, w io.Writer, mask LogMask) *AsyncLogger {
var l *AsyncLogger
var f *os.File
var ok bool
var use_color bool
@@ -73,7 +80,7 @@ func NewLogger(id string, w io.Writer, mask LogMask) *Logger {
f, ok = w.(*os.File)
if ok { use_color = _is_ansi_tty(f.Fd()) }
l = &Logger{
l = &AsyncLogger{
id: id,
out: w,
mask: mask,
@@ -86,8 +93,8 @@ func NewLogger(id string, w io.Writer, mask LogMask) *Logger {
return l
}
func NewLoggerToFile(id string, file_name string, max_size int64, rotate int, mask LogMask) (*Logger, error) {
var l *Logger
func NewAsyncLoggerToFile(id string, file_name string, max_size int64, rotate int, mask LogMask) (*AsyncLogger, error) {
var l *AsyncLogger
var f *os.File
var fi os.FileInfo
var err error
@@ -102,7 +109,7 @@ func NewLoggerToFile(id string, file_name string, max_size int64, rotate int, ma
rotate = 0
}
l = &Logger{
l = &AsyncLogger{
id: id,
out: f,
mask: mask,
@@ -119,7 +126,7 @@ func NewLoggerToFile(id string, file_name string, max_size int64, rotate int, ma
return l, nil
}
func (l *Logger) Close() {
func (l *AsyncLogger) Close() {
if l.closed.CompareAndSwap(false, true) {
l.msg_chan <- logger_msg_t{code: 1}
l.wg.Wait()
@@ -127,11 +134,11 @@ func (l *Logger) Close() {
}
}
func (l *Logger) Rotate() {
func (l *AsyncLogger) Rotate() {
l.msg_chan <- logger_msg_t{code: 2}
}
func (l *Logger) logger_task() {
func (l *AsyncLogger) logger_task() {
var msg logger_msg_t
defer l.wg.Done()
@@ -160,17 +167,17 @@ main_loop:
}
}
func (l *Logger) Write(id string, level LogLevel, fmtstr string, args ...interface{}) {
func (l *AsyncLogger) Write(id string, level LogLevel, fmtstr string, args ...interface{}) {
if l.mask & LogMask(level) == 0 { return }
l.write(id, level, 1, fmtstr, args...)
}
func (l *Logger) WriteWithCallDepth(id string, level LogLevel, call_depth int, fmtstr string, args ...interface{}) {
func (l *AsyncLogger) WriteWithCallDepth(id string, level LogLevel, call_depth int, fmtstr string, args ...interface{}) {
if l.mask & LogMask(level) == 0 { return }
l.write(id, level, call_depth + 1, fmtstr, args...)
}
func (l *Logger) write(id string, level LogLevel, call_depth int, fmtstr string, args ...interface{}) {
func (l *AsyncLogger) write(id string, level LogLevel, call_depth int, fmtstr string, args ...interface{}) {
var now time.Time
var off_m int
var off_h int
@@ -223,7 +230,7 @@ func (l *Logger) write(id string, level LogLevel, call_depth int, fmtstr string,
l.msg_chan <- logger_msg_t{ code: 0, data: sb.String() }
}
func (l *Logger) rotate() {
func (l *AsyncLogger) rotate() {
var f *os.File
var fi os.FileInfo
var i int
@@ -259,7 +266,7 @@ func (l *Logger) rotate() {
}
}
func (l* Logger) log_level_to_ansi_code(level LogLevel) string {
func (l* AsyncLogger) log_level_to_ansi_code(level LogLevel) string {
switch level {
case LOG_ERROR:
return "\x1B[31m" // red
+6
View File
@@ -0,0 +1,6 @@
package codit
import "embed"
//go:embed migrations/*.sql
var migrationFiles embed.FS
+2177
View File
File diff suppressed because it is too large Load Diff
+80
View File
@@ -12,6 +12,86 @@ The current baseline is a single clean initialization migration. Future schema c
- JSON arrays and objects are stored as text columns with names ending in `_json`.
- Secrets and private keys are currently stored in database text columns. Treat database backups as sensitive material.
## Relationship Diagrams
The diagrams below are intentionally split by domain. They show the main logical relationships; a few relationships are implemented through `public_id` string columns rather than strict integer foreign keys.
### Identity And Authorization
```mermaid
erDiagram
users ||--o{ sessions : owns
users ||--o{ api_keys : owns
users ||--o{ user_group_members : joins
user_groups ||--o{ user_group_members : contains
service_principals ||--o{ principal_api_keys : owns
service_principals ||--o{ cert_principal_bindings : maps
service_principals ||--o{ principal_project_roles : assigned
users ||..o{ subject_permissions : subject
user_groups ||..o{ subject_permissions : subject
service_principals ||..o{ subject_permissions : subject
```
### Projects And Repository Content
```mermaid
erDiagram
users ||--o{ projects : creates
users ||--o{ project_members : member
projects ||--o{ project_members : has
projects ||--o{ project_role_bindings : grants
projects ||--o{ repos : owns
projects ||--o{ project_repos : attaches
repos ||--o{ project_repos : visible_as
projects ||--o{ issues : tracks
issues ||--o{ issue_comments : has
projects ||--o{ wiki_pages : documents
projects ||--o{ uploads : stores
```
### PKI And ACME
```mermaid
erDiagram
pki_cas ||--o{ pki_cas : parent
pki_cas ||--o{ pki_certs : issues
pki_cas ||--o{ pki_client_profiles : backs
pki_client_profiles ||--o{ pki_client_profile_targets : targets
pki_client_profiles ||--o{ pki_client_issuances : records
pki_certs ||..o{ pki_client_issuances : cert_snapshot
acme_profiles ||--o{ acme_orders : creates
pki_certs ||..o{ acme_orders : result_cert
```
### SSH Certificates And Web SSH Broker
```mermaid
erDiagram
ssh_user_cas ||--o{ ssh_user_ca_issuances : signs
users ||..o{ ssh_user_ca_issuances : issuer
ssh_principal_grants ||--o{ ssh_principal_grant_targets : targets
ssh_principal_grants ||..o{ ssh_user_ca_issuances : authorizes
ssh_servers ||--o{ ssh_server_host_keys : pins
ssh_servers ||--o{ ssh_access_profiles : exposes
ssh_secrets ||..o{ ssh_access_profiles : credential
ssh_user_cas ||..o{ ssh_access_profiles : managed_cert_ca
ssh_access_profiles ||--o{ ssh_access_profile_targets : targets
ssh_access_profiles ||--o{ ssh_sessions : starts
ssh_servers ||--o{ ssh_sessions : target
users ||--o{ ssh_sessions : opens
```
### TLS And RPM Mirror State
```mermaid
erDiagram
tls_auth_policies ||..o{ tls_listeners : referenced_by
pki_certs ||..o{ tls_listeners : server_cert
pki_cas ||..o{ tls_listeners : client_ca
repos ||--o{ rpm_repo_dirs : contains
rpm_repo_dirs ||--o{ rpm_mirror_runs : records
```
## Core Identity And Auth
### `users`