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"` RPMHTTPPrefix string `json:"rpm_http_prefix"`
} }
const DefaultEnvPrefix string = "CODIT_"
type Duration time.Duration
func Load(path string) (Config, error) { func Load(path string) (Config, error) {
return LoadWithEnvPrefix(path, DefaultEnvPrefix)
}
func LoadWithEnvPrefix(path string, envPrefix string) (Config, error) {
var cfg Config var cfg Config
var data []byte var data []byte
var err error var err error
@@ -55,7 +63,7 @@ func Load(path string) (Config, error) {
DataDir: "./codit-data", DataDir: "./codit-data",
FrontendDir: filepath.Join("..", "frontend", "dist"), FrontendDir: filepath.Join("..", "frontend", "dist"),
DBDriver: "sqlite", DBDriver: "sqlite",
DBDSN: "file:./codit-data/codit.db?_pragma=foreign_keys(1)", DBDSN: "file:./codit-data/codit.db",
SessionTTL: Duration(24 * time.Hour), SessionTTL: Duration(24 * time.Hour),
AuthMode: "db", AuthMode: "db",
LDAPUserFilter: "(uid={username})", LDAPUserFilter: "(uid={username})",
@@ -76,7 +84,7 @@ func Load(path string) (Config, error) {
return cfg, err return cfg, err
} }
} }
override(&cfg) override(&cfg, envPrefix)
cfg.AuthMode = strings.ToLower(strings.TrimSpace(cfg.AuthMode)) cfg.AuthMode = strings.ToLower(strings.TrimSpace(cfg.AuthMode))
cfg.TLSServerCertSource = strings.ToLower(strings.TrimSpace(cfg.TLSServerCertSource)) cfg.TLSServerCertSource = strings.ToLower(strings.TrimSpace(cfg.TLSServerCertSource))
cfg.TLSClientAuth = strings.ToLower(strings.TrimSpace(cfg.TLSClientAuth)) cfg.TLSClientAuth = strings.ToLower(strings.TrimSpace(cfg.TLSClientAuth))
@@ -91,143 +99,147 @@ func Load(path string) (Config, error) {
return cfg, nil return cfg, nil
} }
func override(cfg *Config) { func override(cfg *Config, envPrefix string) {
var v string var v string
v = os.Getenv("CODIT_HTTP_ADDRS") v = getEnv(envPrefix, "HTTP_ADDRS")
if v != "" { if v != "" {
cfg.HTTPAddrs = splitCSV(v) cfg.HTTPAddrs = splitCSV(v)
} }
v = os.Getenv("CODIT_HTTPS_ADDRS") v = getEnv(envPrefix, "HTTPS_ADDRS")
if v != "" { if v != "" {
cfg.HTTPSAddrs = splitCSV(v) cfg.HTTPSAddrs = splitCSV(v)
} }
v = os.Getenv("CODIT_PUBLIC_BASE_URL") v = getEnv(envPrefix, "PUBLIC_BASE_URL")
if v != "" { if v != "" {
cfg.PublicBaseURL = v cfg.PublicBaseURL = v
} }
v = os.Getenv("CODIT_DATA_DIR") v = getEnv(envPrefix, "DATA_DIR")
if v != "" { if v != "" {
cfg.DataDir = v cfg.DataDir = v
} }
v = os.Getenv("CODIT_FRONTEND_DIR") v = getEnv(envPrefix, "FRONTEND_DIR")
if v != "" { if v != "" {
cfg.FrontendDir = v cfg.FrontendDir = v
} }
v = os.Getenv("CODIT_DB_DRIVER") v = getEnv(envPrefix, "DB_DRIVER")
if v != "" { if v != "" {
cfg.DBDriver = v cfg.DBDriver = v
} }
v = os.Getenv("CODIT_DB_DSN") v = getEnv(envPrefix, "DB_DSN")
if v != "" { if v != "" {
cfg.DBDSN = v cfg.DBDSN = v
} }
v = os.Getenv("CODIT_AUTH_MODE") v = getEnv(envPrefix, "AUTH_MODE")
if v != "" { if v != "" {
cfg.AuthMode = v cfg.AuthMode = v
} }
v = os.Getenv("CODIT_LDAP_URL") v = getEnv(envPrefix, "LDAP_URL")
if v != "" { if v != "" {
cfg.LDAPURL = v cfg.LDAPURL = v
} }
v = os.Getenv("CODIT_LDAP_BIND_DN") v = getEnv(envPrefix, "LDAP_BIND_DN")
if v != "" { if v != "" {
cfg.LDAPBindDN = v cfg.LDAPBindDN = v
} }
v = os.Getenv("CODIT_LDAP_BIND_PASSWORD") v = getEnv(envPrefix, "LDAP_BIND_PASSWORD")
if v != "" { if v != "" {
cfg.LDAPBindPassword = v cfg.LDAPBindPassword = v
} }
v = os.Getenv("CODIT_LDAP_USER_BASE_DN") v = getEnv(envPrefix, "LDAP_USER_BASE_DN")
if v != "" { if v != "" {
cfg.LDAPUserBaseDN = v cfg.LDAPUserBaseDN = v
} }
v = os.Getenv("CODIT_LDAP_USER_FILTER") v = getEnv(envPrefix, "LDAP_USER_FILTER")
if v != "" { if v != "" {
cfg.LDAPUserFilter = v cfg.LDAPUserFilter = v
} }
v = os.Getenv("CODIT_LDAP_TLS_INSECURE_SKIP_VERIFY") v = getEnv(envPrefix, "LDAP_TLS_INSECURE_SKIP_VERIFY")
if v != "" { if v != "" {
cfg.LDAPTLSInsecureSkipVerify = parseEnvBool(v) cfg.LDAPTLSInsecureSkipVerify = parseEnvBool(v)
} }
v = os.Getenv("CODIT_OIDC_CLIENT_ID") v = getEnv(envPrefix, "OIDC_CLIENT_ID")
if v != "" { if v != "" {
cfg.OIDCClientID = v cfg.OIDCClientID = v
} }
v = os.Getenv("CODIT_OIDC_CLIENT_SECRET") v = getEnv(envPrefix, "OIDC_CLIENT_SECRET")
if v != "" { if v != "" {
cfg.OIDCClientSecret = v cfg.OIDCClientSecret = v
} }
v = os.Getenv("CODIT_OIDC_AUTHORIZE_URL") v = getEnv(envPrefix, "OIDC_AUTHORIZE_URL")
if v != "" { if v != "" {
cfg.OIDCAuthorizeURL = v cfg.OIDCAuthorizeURL = v
} }
v = os.Getenv("CODIT_OIDC_TOKEN_URL") v = getEnv(envPrefix, "OIDC_TOKEN_URL")
if v != "" { if v != "" {
cfg.OIDCTokenURL = v cfg.OIDCTokenURL = v
} }
v = os.Getenv("CODIT_OIDC_USERINFO_URL") v = getEnv(envPrefix, "OIDC_USERINFO_URL")
if v != "" { if v != "" {
cfg.OIDCUserInfoURL = v cfg.OIDCUserInfoURL = v
} }
v = os.Getenv("CODIT_OIDC_REDIRECT_URL") v = getEnv(envPrefix, "OIDC_REDIRECT_URL")
if v != "" { if v != "" {
cfg.OIDCRedirectURL = v cfg.OIDCRedirectURL = v
} }
v = os.Getenv("CODIT_OIDC_SCOPES") v = getEnv(envPrefix, "OIDC_SCOPES")
if v != "" { if v != "" {
cfg.OIDCScopes = v cfg.OIDCScopes = v
} }
v = os.Getenv("CODIT_OIDC_ENABLED") v = getEnv(envPrefix, "OIDC_ENABLED")
if v != "" { if v != "" {
cfg.OIDCEnabled = parseEnvBool(v) cfg.OIDCEnabled = parseEnvBool(v)
} }
v = os.Getenv("CODIT_OIDC_TLS_INSECURE_SKIP_VERIFY") v = getEnv(envPrefix, "OIDC_TLS_INSECURE_SKIP_VERIFY")
if v != "" { if v != "" {
cfg.OIDCTLSInsecureSkipVerify = parseEnvBool(v) cfg.OIDCTLSInsecureSkipVerify = parseEnvBool(v)
} }
v = os.Getenv("CODIT_TLS_SERVER_CERT_SOURCE") v = getEnv(envPrefix, "TLS_SERVER_CERT_SOURCE")
if v != "" { if v != "" {
cfg.TLSServerCertSource = v cfg.TLSServerCertSource = v
} }
v = os.Getenv("CODIT_TLS_CERT_FILE") v = getEnv(envPrefix, "TLS_CERT_FILE")
if v != "" { if v != "" {
cfg.TLSCertFile = v cfg.TLSCertFile = v
} }
v = os.Getenv("CODIT_TLS_KEY_FILE") v = getEnv(envPrefix, "TLS_KEY_FILE")
if v != "" { if v != "" {
cfg.TLSKeyFile = v cfg.TLSKeyFile = v
} }
v = os.Getenv("CODIT_TLS_PKI_SERVER_CERT_ID") v = getEnv(envPrefix, "TLS_PKI_SERVER_CERT_ID")
if v != "" { if v != "" {
cfg.TLSPKIServerCertID = v cfg.TLSPKIServerCertID = v
} }
v = os.Getenv("CODIT_TLS_CLIENT_AUTH") v = getEnv(envPrefix, "TLS_CLIENT_AUTH")
if v != "" { if v != "" {
cfg.TLSClientAuth = v cfg.TLSClientAuth = v
} }
v = os.Getenv("CODIT_TLS_CLIENT_CA_FILE") v = getEnv(envPrefix, "TLS_CLIENT_CA_FILE")
if v != "" { if v != "" {
cfg.TLSClientCAFile = v cfg.TLSClientCAFile = v
} }
v = os.Getenv("CODIT_TLS_PKI_CLIENT_CA_ID") v = getEnv(envPrefix, "TLS_PKI_CLIENT_CA_ID")
if v != "" { if v != "" {
cfg.TLSPKIClientCAID = v cfg.TLSPKIClientCAID = v
} }
v = os.Getenv("CODIT_TLS_MIN_VERSION") v = getEnv(envPrefix, "TLS_MIN_VERSION")
if v != "" { if v != "" {
cfg.TLSMinVersion = v cfg.TLSMinVersion = v
} }
v = os.Getenv("CODIT_GIT_HTTP_PREFIX") v = getEnv(envPrefix, "GIT_HTTP_PREFIX")
if v != "" { if v != "" {
cfg.GitHTTPPrefix = v cfg.GitHTTPPrefix = v
} }
v = os.Getenv("CODIT_RPM_HTTP_PREFIX") v = getEnv(envPrefix, "RPM_HTTP_PREFIX")
if v != "" { if v != "" {
cfg.RPMHTTPPrefix = 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 { func (d Duration) Duration() time.Duration {
return time.Duration(d) return time.Duration(d)
+1 -1
View File
@@ -5,7 +5,7 @@ import "encoding/base64"
import "errors" import "errors"
import "time" import "time"
import "codit/internal/config" import "codit/config"
import "golang.org/x/crypto/bcrypt" import "golang.org/x/crypto/bcrypt"
func HashPassword(password string) (string, error) { func HashPassword(password string) (string, error) {
+1 -1
View File
@@ -4,7 +4,7 @@ import "strings"
import "testing" import "testing"
import "time" import "time"
import "codit/internal/config" import "codit/config"
func TestHashAndComparePassword(t *testing.T) { func TestHashAndComparePassword(t *testing.T) {
var hash string var hash string
+1 -1
View File
@@ -7,7 +7,7 @@ import "strings"
import "time" import "time"
import "crypto/tls" import "crypto/tls"
import "codit/internal/config" import "codit/config"
import "github.com/go-ldap/ldap/v3" import "github.com/go-ldap/ldap/v3"
type LDAPUser struct { 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 "context"
import "database/sql" import "database/sql"
import "fmt" import "fmt"
import "io/fs"
import "os" import "os"
import "path/filepath" import "path/filepath"
import "sort" import "sort"
@@ -239,6 +240,86 @@ func (s *Store) ApplyMigrations(dir string) error {
return nil 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) { func (s *Store) hasCoreSchema() (bool, error) {
var row *sql.Row var row *sql.Row
var value int var value int
@@ -340,6 +421,31 @@ func listMigrationFiles(dir string) ([]string, error) {
return files, nil 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 { func migrationVersionFromPath(path string) string {
return strings.TrimSuffix(filepath.Base(path), filepath.Ext(path)) 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/middleware"
import "codit/internal/models" import "codit/internal/models"
import "codit/internal/util" import "codit/internal/util"
import codit_logger "codit/logger"
type AuthFunc func(username, password string) (bool, error) type AuthFunc func(username, password string) (bool, error)
@@ -23,12 +24,12 @@ type HTTPServer struct {
store *db.Store store *db.Store
baseDir string baseDir string
auth AuthFunc auth AuthFunc
logger *util.Logger logger codit_logger.Logger
} }
const logIDDocker string = "docker" 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{ return &HTTPServer{
store: store, store: store,
baseDir: baseDir, baseDir: baseDir,
@@ -59,7 +60,7 @@ func (s *HTTPServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if !hasLogInfo { if !hasLogInfo {
logInfo = RequestLogInfo{OrgPath: "-", ProjectID: "-", ProjectSlug: "-", RepoID: "-", RepoName: "-"} 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) 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 return
@@ -74,7 +75,7 @@ func (s *HTTPServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if !hasLogInfo { if !hasLogInfo {
logInfo = RequestLogInfo{OrgPath: "-", ProjectID: "-", ProjectSlug: "-", RepoID: "-", RepoName: "-"} 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) 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 return
@@ -88,7 +89,7 @@ func (s *HTTPServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if !hasLogInfo { if !hasLogInfo {
logInfo = RequestLogInfo{OrgPath: "-", ProjectID: "-", ProjectSlug: "-", RepoID: "-", RepoName: "-"} 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) 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 package git
import codit_logger "codit/logger"
import "codit/internal/middleware" import "codit/internal/middleware"
import "codit/internal/util"
import "net/http" import "net/http"
import "os" import "os"
import "path/filepath" import "path/filepath"
@@ -12,12 +12,12 @@ import "github.com/sosedoff/gitkit"
type HTTPServer struct { type HTTPServer struct {
handler http.Handler handler http.Handler
baseDir string baseDir string
logger *util.Logger logger codit_logger.Logger
} }
type AuthFunc func(username, password string) (bool, error) 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 cfg gitkit.Config
var srv *gitkit.Server var srv *gitkit.Server
cfg = gitkit.Config{ cfg = gitkit.Config{
@@ -74,7 +74,7 @@ func (s *HTTPServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
} }
mtlsInfo = middleware.ClientCertLogFields(r) mtlsInfo = middleware.ClientCertLogFields(r)
s.logger.Write( 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) 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 package handlers
import codit_logger "codit/logger"
import "context" import "context"
import "bytes" import "bytes"
import "crypto/ecdsa" import "crypto/ecdsa"
@@ -21,7 +22,6 @@ import "time"
import "golang.org/x/crypto/acme" import "golang.org/x/crypto/acme"
import "codit/internal/models" import "codit/internal/models"
import "codit/internal/util"
type acmeProfileRequest struct { type acmeProfileRequest struct {
Name string `json:"name"` 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"}) WriteJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid request"})
return 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) item, err = normalizeACMEProfileRequest(req)
if err != nil { if err != nil {
WriteJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()}) 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.LastError = ""
item, err = api.store(r).CreateACMEProfile(item) item, err = api.store(r).CreateACMEProfile(item)
if err != nil { 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()}) WriteJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
return 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) out = buildACMEProfileSummary(item)
WriteJSON(w, http.StatusCreated, out) 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.Enabled = req.Enabled
item, err = api.store(r).UpdateACMEProfile(item) item, err = api.store(r).UpdateACMEProfile(item)
if err != nil { 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()}) WriteJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
return 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) out = buildACMEProfileSummary(item)
WriteJSON(w, http.StatusOK, out) 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"]) err = api.store(r).DeleteACMEProfile(params["id"])
if err != nil { 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()}) WriteJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
return 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"}) 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) 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) order, err = api.createACMEOrder(r.Context(), prev.ProfileID, cert.CommonName, sans, cert.ID)
if err != nil { if err != nil {
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
@@ -362,7 +362,7 @@ func (api *API) createACMEOrder(ctx context.Context, profileID string, commonNam
var certKeyPEM string var certKeyPEM string
var csrPEM string var csrPEM string
var err error 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) == "" { if strings.TrimSpace(profileID) == "" {
return order, errors.New("profile_id is required") 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) client, accountKey, profile, err = api.ensureACMEAccount(ctx, profile)
if err != nil { 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()) _ = api.storeContext(ctx).UpdateACMEProfileLastError(profile.ID, err.Error())
return order, err return order, err
} }
_ = accountKey _ = 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) rawOrder, err = client.AuthorizeOrder(context.Background(), domainIDs)
if err != nil { 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()) _ = api.storeContext(ctx).UpdateACMEProfileLastError(profile.ID, err.Error())
return order, err 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++ { for i = 0; i < len(rawOrder.AuthzURLs); i++ {
authz, err = client.GetAuthorization(context.Background(), rawOrder.AuthzURLs[i]) authz, err = client.GetAuthorization(context.Background(), rawOrder.AuthzURLs[i])
if err != nil { if err != nil {
@@ -430,7 +430,7 @@ func (api *API) createACMEOrder(ctx context.Context, profileID string, commonNam
DNSValue: txt, DNSValue: txt,
Status: ch.Status, 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{ order = models.ACMEOrder{
ProfileID: profile.ID, ProfileID: profile.ID,
@@ -447,10 +447,10 @@ func (api *API) createACMEOrder(ctx context.Context, profileID string, commonNam
} }
order, err = api.storeContext(ctx).CreateACMEOrder(order) order, err = api.storeContext(ctx).CreateACMEOrder(order)
if err != nil { 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 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, "") _ = api.storeContext(ctx).UpdateACMEProfileLastError(profile.ID, "")
return order, nil 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"}) WriteJSON(w, http.StatusBadRequest, map[string]string{"error": "renew_mode must be override or new_cert"})
return 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"]) order, err = api.store(r).GetACMEOrder(params["id"])
if err != nil { if err != nil {
if err == sql.ErrNoRows { 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) client, accountKey, profile, err = api.ensureACMEAccount(r.Context(), profile)
if err != nil { 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.Status = "failed"
order.LastError = err.Error() order.LastError = err.Error()
_, _ = api.store(r).UpdateACMEOrder(order) _, _ = 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 { if authz.Status == acme.StatusValid {
order.Challenges[i].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 continue
} }
if profile.SolverType == "acme_dns" { if profile.SolverType == "acme_dns" {
err = api.updateACMEDNSRecord(profile, order.Challenges[i]) err = api.updateACMEDNSRecord(profile, order.Challenges[i])
if err != nil { 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.Status = "failed"
order.LastError = err.Error() order.LastError = err.Error()
order.Challenges[i].Status = "failed" 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()}) WriteJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
return 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 = &acme.Challenge{Type: "dns-01", URI: order.Challenges[i].ChallengeURL, Token: order.Challenges[i].Token}
challenge, err = client.Accept(context.Background(), challenge) 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) authz, err = client.WaitAuthorization(context.Background(), order.Challenges[i].AuthorizationURL)
if err != nil { 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.Status = "failed"
order.LastError = err.Error() order.LastError = err.Error()
order.Challenges[i].Status = "failed" order.Challenges[i].Status = "failed"
@@ -582,11 +582,11 @@ func (api *API) FinalizeACMEOrder(w http.ResponseWriter, r *http.Request, params
break 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) certDER, certURL, err = client.CreateOrderCert(context.Background(), order.FinalizeURL, decodeCSRPEM(order.CSRPEM), true)
if err != nil { 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.Status = "failed"
order.LastError = err.Error() order.LastError = err.Error()
_, _ = api.store(r).UpdateACMEOrder(order) _, _ = 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.CAID = existing.CAID
cert, err = api.store(r).ReplacePKICert(cert) cert, err = api.store(r).ReplacePKICert(cert)
if err != nil { 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.Status = "failed"
order.LastError = err.Error() order.LastError = err.Error()
_, _ = api.store(r).UpdateACMEOrder(order) _, _ = api.store(r).UpdateACMEOrder(order)
WriteJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()}) WriteJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
return 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) == "" { if strings.TrimSpace(cert.ID) == "" {
cert, err = api.store(r).CreatePKICert(cert) cert, err = api.store(r).CreatePKICert(cert)
if err != nil { 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.Status = "failed"
order.LastError = err.Error() order.LastError = err.Error()
_, _ = api.store(r).UpdateACMEOrder(order) _, _ = 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, "") _ = api.store(r).UpdateACMEProfileLastError(profile.ID, "")
_ = certURL _ = 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) 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"]) err = api.store(r).DeleteACMEOrder(params["id"])
if err != nil { 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()}) WriteJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
return 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"}) WriteJSON(w, http.StatusOK, map[string]string{"status": "deleted"})
} }
+16 -15
View File
@@ -19,7 +19,7 @@ import "time"
import "unicode" import "unicode"
import "codit/internal/auth" import "codit/internal/auth"
import "codit/internal/config" import "codit/config"
import "codit/internal/db" import "codit/internal/db"
import "codit/internal/docker" import "codit/internal/docker"
import "codit/internal/git" import "codit/internal/git"
@@ -28,6 +28,7 @@ import "codit/internal/models"
import "codit/internal/rpm" import "codit/internal/rpm"
import "codit/internal/storage" import "codit/internal/storage"
import "codit/internal/util" import "codit/internal/util"
import codit_logger "codit/logger"
type API struct { type API struct {
Cfg config.Config Cfg config.Config
@@ -38,7 +39,7 @@ type API struct {
RpmMirror *rpm.MirrorManager RpmMirror *rpm.MirrorManager
DockerBase string DockerBase string
Uploads storage.FileStore Uploads storage.FileStore
Logger *util.Logger Logger codit_logger.Logger
SSHSessionRegistry *SSHSessionRegistry SSHSessionRegistry *SSHSessionRegistry
SSHPromptedAuthStore *SSHPromptedAuthStore SSHPromptedAuthStore *SSHPromptedAuthStore
SSHPreparedSessionStore *SSHPreparedSessionStore SSHPreparedSessionStore *SSHPreparedSessionStore
@@ -386,10 +387,10 @@ func (api *API) Login(w http.ResponseWriter, r *http.Request, _ map[string]strin
authCfg, _ = api.effectiveAuthConfig(r.Context()) authCfg, _ = api.effectiveAuthConfig(r.Context())
if authCfg.AuthMode == "ldap" || authCfg.AuthMode == "hybrid" { 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) ldapUser, err = auth.LDAPAuthenticateContext(r.Context(), authCfg, req.Username, req.Password)
if err == nil { 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 { if user.ID != "" && user.Disabled {
WriteJSON(w, http.StatusForbidden, map[string]string{"error": "user disabled"}) WriteJSON(w, http.StatusForbidden, map[string]string{"error": "user disabled"})
return return
@@ -413,7 +414,7 @@ func (api *API) Login(w http.ResponseWriter, r *http.Request, _ map[string]strin
WriteJSON(w, http.StatusOK, user) WriteJSON(w, http.StatusOK, user)
return 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"}) 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()) user, ok = middleware.UserFromContext(r.Context())
if ok { 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()) settings, err = api.getMergedAuthSettings(r.Context())
if err != nil { 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()}) WriteJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
return 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) 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" settings.OIDCScopes = "openid profile email"
} }
if ok { 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) user.Username, settings.AuthMode, settings.OIDCEnabled, settings.LDAPURL, settings.OIDCAuthorizeURL)
} }
err = api.store(r).SetAuthSettings(settings) err = api.store(r).SetAuthSettings(settings)
if err != nil { 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()}) WriteJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
return 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) 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 cfg.LDAPUserFilter = merged.LDAPUserFilter
err = auth.LDAPTestConnectionContext(r.Context(), cfg) err = auth.LDAPTestConnectionContext(r.Context(), cfg)
if err != nil { 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()}) WriteJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
return 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) != "" { if strings.TrimSpace(req.Username) != "" && strings.TrimSpace(req.Password) != "" {
user, err = auth.LDAPAuthenticateContext(r.Context(), cfg, strings.TrimSpace(req.Username), req.Password) user, err = auth.LDAPAuthenticateContext(r.Context(), cfg, strings.TrimSpace(req.Username), req.Password)
if err != nil { 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()}) WriteJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
return 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}) WriteJSON(w, http.StatusOK, map[string]string{"status": "ok", "user": user.Username})
return return
} }
+7 -7
View File
@@ -1,5 +1,6 @@
package handlers package handlers
import codit_logger "codit/logger"
import "context" import "context"
import "crypto/rand" import "crypto/rand"
import "crypto/sha1" import "crypto/sha1"
@@ -16,9 +17,8 @@ import "net/url"
import "strings" import "strings"
import "time" import "time"
import "codit/internal/config" import "codit/config"
import "codit/internal/models" import "codit/internal/models"
import "codit/internal/util"
type oidcTokenResponse struct { type oidcTokenResponse struct {
AccessToken string `json:"access_token"` 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, SameSite: http.SameSiteLaxMode,
}) })
authURL = api.buildOIDCAuthorizeURL(cfg, state) 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) 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) token, err = api.oidcExchangeCode(r.Context(), cfg, code)
if err != nil { 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()}) WriteJSON(w, http.StatusUnauthorized, map[string]string{"error": err.Error()})
return return
} }
claims, err = api.oidcResolveClaims(r.Context(), cfg, token) claims, err = api.oidcResolveClaims(r.Context(), cfg, token)
if err != nil { 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"}) WriteJSON(w, http.StatusUnauthorized, map[string]string{"error": "oidc claims fetch failed"})
return return
} }
user, err = api.oidcGetOrCreateUser(r.Context(), claims) user, err = api.oidcGetOrCreateUser(r.Context(), claims)
if err != nil { 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"}) WriteJSON(w, http.StatusUnauthorized, map[string]string{"error": "oidc user mapping failed"})
return return
} }
api.issueSession(w, r, user) 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) http.Redirect(w, r, "/", http.StatusFound)
} }
+2 -2
View File
@@ -1,5 +1,6 @@
package handlers package handlers
import codit_logger "codit/logger"
import "archive/zip" import "archive/zip"
import "bytes" import "bytes"
import "context" import "context"
@@ -27,7 +28,6 @@ import "time"
import "codit/internal/middleware" import "codit/internal/middleware"
import "codit/internal/models" import "codit/internal/models"
import "codit/internal/pkiutil" import "codit/internal/pkiutil"
import "codit/internal/util"
type pkiCreateRootRequest struct { type pkiCreateRootRequest struct {
Name string `json:"name"` Name string `json:"name"`
@@ -1058,7 +1058,7 @@ func (api *API) DownloadPKICertBundle(w http.ResponseWriter, r *http.Request, pa
chainCount++ 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) data, err = buildPKIZipBundle(cert.CommonName, cert.CertPEM, cert.KeyPEM, false, caPEMs)
if err != nil { if err != nil {
WriteJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()}) WriteJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
@@ -1,5 +1,6 @@
package handlers package handlers
import codit_logger "codit/logger"
import "crypto/rand" import "crypto/rand"
import "crypto/rsa" import "crypto/rsa"
import "crypto/x509" import "crypto/x509"
@@ -20,7 +21,6 @@ import "time"
import "codit/internal/middleware" import "codit/internal/middleware"
import "codit/internal/models" import "codit/internal/models"
import "codit/internal/pkiutil" import "codit/internal/pkiutil"
import "codit/internal/util"
const logIDPKI string = "pki" const logIDPKI string = "pki"
const pkiClientMinValidSeconds int64 = 60 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) item, err = api.store(r).CreatePKIClientProfile(item)
if err != nil { 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()}) WriteJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
return 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)) 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) existing, err = api.store(r).UpdatePKIClientProfile(existing)
if err != nil { 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()}) WriteJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
return 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)) 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"]) err = api.store(r).DeletePKIClientProfile(params["id"])
if err != nil { 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()}) WriteJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
return 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"}) 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) serialHex = strconv.FormatInt(serial, 16)
certPEM, keyPEM, notBefore, notAfter, err = issuePKIClientCertFromProfile(ca, serial, user, profile, permissions, commonName, sanDNS, sanIPs, sanURI, validSeconds) certPEM, keyPEM, notBefore, notAfter, err = issuePKIClientCertFromProfile(ca, serial, user, profile, permissions, commonName, sanDNS, sanIPs, sanURI, validSeconds)
if err != nil { 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()}) WriteJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
return 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()}) WriteJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
return 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{ WriteJSON(w, http.StatusCreated, pkiClientIssueResponse{
Issuance: buildPKIClientIssuanceSummary(issuance), Issuance: buildPKIClientIssuanceSummary(issuance),
CertPEM: cert.CertPEM, 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()}) WriteJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
return 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"}) WriteJSON(w, http.StatusOK, map[string]string{"status": "revoked"})
} }
+13 -13
View File
@@ -1,5 +1,6 @@
package handlers package handlers
import codit_logger "codit/logger"
import "database/sql" import "database/sql"
import "encoding/base64" import "encoding/base64"
import "errors" import "errors"
@@ -19,7 +20,6 @@ import "golang.org/x/net/websocket"
import "codit/internal/db" import "codit/internal/db"
import "codit/internal/middleware" import "codit/internal/middleware"
import "codit/internal/models" import "codit/internal/models"
import "codit/internal/util"
const logIDSSHBroker string = "ssh-broker" const logIDSSHBroker string = "ssh-broker"
type sshServerRequest struct { type sshServerRequest struct {
@@ -122,7 +122,7 @@ const sshSecondFactorNone string = "none"
const sshSecondFactorPromptedTOTP string = "prompted_totp" const sshSecondFactorPromptedTOTP string = "prompted_totp"
func (api *API) logSSHSessionConnectStart(sessionItem models.SSHSession, profile models.SSHAccessProfile, user models.User) { 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", "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.ID,
sessionItem.RemoteAddr, 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) { 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", "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, sessionID,
remoteAddr, 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) { 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", "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.ID,
sessionItem.RemoteAddr, 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) { 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", "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.ID,
sessionItem.RemoteAddr, 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) { 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", "connect failed session=%s requester=%s user=%s profile=%s stage=%s err=%v",
sessionID, sessionID,
remoteAddr, remoteAddr,
@@ -206,7 +206,7 @@ func (api *API) logSSHSessionClosed(sessionItem models.SSHSession, profile model
if connectedAt > 0 && endedAt >= connectedAt { if connectedAt > 0 && endedAt >= connectedAt {
durationSeconds = 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", "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.ID,
sessionItem.RemoteAddr, sessionItem.RemoteAddr,
@@ -1860,7 +1860,7 @@ event_loop:
for { for {
select { select {
case err = <-controlErrCh: 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", "workspace stream closed requester=%s user=%s err=%v",
remoteAddr, remoteAddr,
user.Username, user.Username,
@@ -1920,7 +1920,7 @@ event_loop:
InputErrCh: make(chan error, 1), InputErrCh: make(chan error, 1),
} }
attachments[msg.SessionID] = attachment 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", "workspace attach session=%s requester=%s user=%s",
msg.SessionID, msg.SessionID,
remoteAddr, remoteAddr,
@@ -2172,7 +2172,7 @@ func (api *API) runSSHSessionStream(store *db.Store, user models.User, sessionIt
} }
transcriptRecorder, err = api.openSSHSessionTranscriptRecorder(sessionItem.ID) transcriptRecorder, err = api.openSSHSessionTranscriptRecorder(sessionItem.ID)
if err != nil { 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", "transcript open failed session=%s err=%v",
sessionItem.ID, sessionItem.ID,
err) 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", "session loop ended session=%s requester=%s user=%s profile=%s profile_name=%q source=%s wait_err=%v",
sessionItem.ID, sessionItem.ID,
sessionItem.RemoteAddr, sessionItem.RemoteAddr,
@@ -2247,7 +2247,7 @@ func (api *API) runSSHSessionStream(store *db.Store, user models.User, sessionIt
} }
if waitErr != nil && !isSSHSessionTerminalExit(waitErr) { if waitErr != nil && !isSSHSessionTerminalExit(waitErr) {
_ = store.UpdateSSHSessionStatus(sessionItem.ID, "error", connectedFingerprint, connectedAt, now, waitErr.Error()) _ = 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", "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.ID,
sessionItem.RemoteAddr, sessionItem.RemoteAddr,
@@ -2268,7 +2268,7 @@ func (api *API) runSSHSessionStream(store *db.Store, user models.User, sessionIt
return return
} }
if isSSHSessionTerminalExit(waitErr) { 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", "session ended session=%s requester=%s user=%s profile=%s profile_name=%q err=%v",
sessionItem.ID, sessionItem.ID,
sessionItem.RemoteAddr, sessionItem.RemoteAddr,
@@ -1,5 +1,6 @@
package handlers package handlers
import codit_logger "codit/logger"
import "database/sql" import "database/sql"
import "fmt" import "fmt"
import "io" import "io"
@@ -11,7 +12,6 @@ import "sync"
import "codit/internal/middleware" import "codit/internal/middleware"
import "codit/internal/models" import "codit/internal/models"
import "codit/internal/util"
type sshSessionTranscriptRecorder struct { type sshSessionTranscriptRecorder struct {
mu sync.Mutex mu sync.Mutex
@@ -201,7 +201,7 @@ func (api *API) writeSSHSessionTranscriptChunk(sessionID string, data []byte, re
} }
err = recorder.Write(data) err = recorder.Write(data)
if err != nil { 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", "transcript write failed session=%s err=%v",
sessionID, sessionID,
err) err)
@@ -1,5 +1,6 @@
package handlers package handlers
import codit_logger "codit/logger"
import "database/sql" import "database/sql"
import "errors" import "errors"
import "net/http" import "net/http"
@@ -8,7 +9,6 @@ import "time"
import "codit/internal/middleware" import "codit/internal/middleware"
import "codit/internal/models" import "codit/internal/models"
import "codit/internal/util"
type sshPrincipalGrantTargetRequest struct { type sshPrincipalGrantTargetRequest struct {
TargetType string `json:"target_type"` 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) item, err = api.store(r).CreateSSHPrincipalGrant(item)
if err != nil { 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()}) WriteJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
return 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)) WriteJSON(w, http.StatusCreated, buildSSHPrincipalGrantSummary(item))
} }
@@ -256,11 +256,11 @@ func (api *API) UpdateSSHPrincipalGrant(w http.ResponseWriter, r *http.Request,
item.Targets = targets item.Targets = targets
item, err = api.store(r).UpdateSSHPrincipalGrant(item) item, err = api.store(r).UpdateSSHPrincipalGrant(item)
if err != nil { 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()}) WriteJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
return 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)) 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"]) err = api.store(r).DeleteSSHPrincipalGrant(params["id"])
if err != nil { 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()}) WriteJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
return 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"}) WriteJSON(w, http.StatusOK, map[string]string{"status": "deleted"})
} }
+12 -12
View File
@@ -1,5 +1,6 @@
package handlers package handlers
import codit_logger "codit/logger"
import "crypto/ed25519" import "crypto/ed25519"
import "crypto/ecdsa" import "crypto/ecdsa"
import "crypto/elliptic" import "crypto/elliptic"
@@ -22,7 +23,6 @@ import "golang.org/x/crypto/ssh"
import "codit/internal/middleware" import "codit/internal/middleware"
import "codit/internal/models" import "codit/internal/models"
import "codit/internal/util"
type sshUserCACreateRequest struct { type sshUserCACreateRequest struct {
Name string `json:"name"` 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) item, err = api.store(r).CreateSSHUserCA(item)
if err != nil { 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()}) WriteJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
return return
} }
summary = buildSSHUserCASummary(item) 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) 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.MaxUserValidSeconds = maxUserValidSeconds
item, err = api.store(r).UpdateSSHUserCA(item) item, err = api.store(r).UpdateSSHUserCA(item)
if err != nil { 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()}) WriteJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
return 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)) 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"]) err = api.store(r).DeleteSSHUserCA(params["id"])
if err != nil { 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()}) WriteJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
return 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"}) 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) err = api.recordSSHUserCAIssuance(r, item, user, "admin", sourcePublicKey, sourcePublicKeyFingerprint, nil, nil, response)
if err != nil { 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"}) WriteJSON(w, http.StatusInternalServerError, map[string]string{"error": "failed to record issuance audit"})
return 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) WriteJSON(w, http.StatusOK, response)
} }
@@ -720,18 +720,18 @@ func (api *API) SignSSHUserKeyForSelf(w http.ResponseWriter, r *http.Request, pa
for grantID = range selectedGrantIDs { for grantID = range selectedGrantIDs {
err = api.store(r).MarkSSHPrincipalGrantUsed(grantID) err = api.store(r).MarkSSHPrincipalGrantUsed(grantID)
if err != nil { 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"}) WriteJSON(w, http.StatusBadRequest, map[string]string{"error": "selected principal grant is not active"})
return return
} }
} }
err = api.recordSSHUserCAIssuance(r, item, user, "self", sourcePublicKey, sourcePublicKeyFingerprint, selectedIDs, selectedGrantNames, response) err = api.recordSSHUserCAIssuance(r, item, user, "self", sourcePublicKey, sourcePublicKeyFingerprint, selectedIDs, selectedGrantNames, response)
if err != nil { 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"}) WriteJSON(w, http.StatusInternalServerError, map[string]string{"error": "failed to record issuance audit"})
return 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) WriteJSON(w, http.StatusOK, response)
} }
+3 -3
View File
@@ -1,5 +1,6 @@
package middleware package middleware
import codit_logger "codit/logger"
import "bufio" import "bufio"
import "fmt" import "fmt"
import "io" import "io"
@@ -8,7 +9,6 @@ import "net/http"
import "time" import "time"
import "codit/internal/models" import "codit/internal/models"
import "codit/internal/util"
type statusRecorder struct { type statusRecorder struct {
http.ResponseWriter http.ResponseWriter
@@ -75,7 +75,7 @@ func (r *statusRecorder) ReadFrom(src io.Reader) (int64, error) {
return io.Copy(r.ResponseWriter, src) 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) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var recorder *statusRecorder var recorder *statusRecorder
var start time.Time var start time.Time
@@ -104,7 +104,7 @@ func AccessLog(logger *util.Logger, next http.Handler) http.Handler {
if !ok || authReason == "" { if !ok || authReason == "" {
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) 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 package middleware
import codit_logger "codit/logger"
import "bytes" import "bytes"
import "context" import "context"
import "fmt"
import "net/http" import "net/http"
import "net/http/httptest" import "net/http/httptest"
import "strings" import "strings"
import "testing" import "testing"
import "codit/internal/models" 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) { func TestAccessLogWritesRecord(t *testing.T) {
var buf bytes.Buffer var buf bytes.Buffer
var logger *util.Logger var logger codit_logger.Logger
var handler http.Handler var handler http.Handler
var req *http.Request var req *http.Request
var recorder *httptest.ResponseRecorder var recorder *httptest.ResponseRecorder
var user models.User var user models.User
var ctx context.Context var ctx context.Context
logger = util.NewLogger("test", &buf, util.LOG_ALL) logger = &testLogger{buf: &buf}
defer logger.Close() defer logger.Close()
handler = AccessLog(logger, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { handler = AccessLog(logger, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusCreated) 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-Methods", "*")
h.Add("Access-Control-Allow-Origin", "*") h.Add("Access-Control-Allow-Origin", "*")
if r.Method == "OPTIONS" { if r.Method == http.MethodOptions {
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
return return
} }
+6 -6
View File
@@ -1,20 +1,20 @@
package rpm package rpm
import codit_logger "codit/logger"
import "codit/internal/middleware" import "codit/internal/middleware"
import "codit/internal/util"
import "net/http" import "net/http"
type HTTPServer struct { type HTTPServer struct {
handler http.Handler handler http.Handler
auth AuthFunc auth AuthFunc
logger *util.Logger logger codit_logger.Logger
} }
type AuthFunc func(username, password string) (bool, error) type AuthFunc func(username, password string) (bool, error)
const logIDRPM string = "rpm" 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 var server HTTPServer
server = HTTPServer{ server = HTTPServer{
handler: http.FileServer(http.Dir(baseDir)), handler: http.FileServer(http.Dir(baseDir)),
@@ -47,7 +47,7 @@ func (s *HTTPServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if !hasLogInfo { if !hasLogInfo {
logInfo = RequestLogInfo{OrgPath: "-", ProjectID: "-", ProjectSlug: "-", RepoID: "-", RepoName: "-"} 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 return
} }
allowed, err = s.auth(username, password) allowed, err = s.auth(username, password)
@@ -59,7 +59,7 @@ func (s *HTTPServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if !hasLogInfo { if !hasLogInfo {
logInfo = RequestLogInfo{OrgPath: "-", ProjectID: "-", ProjectSlug: "-", RepoID: "-", RepoName: "-"} 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 return
} }
userLabel = username userLabel = username
@@ -69,7 +69,7 @@ func (s *HTTPServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if !hasLogInfo { if !hasLogInfo {
logInfo = RequestLogInfo{OrgPath: "-", ProjectID: "-", ProjectSlug: "-", RepoID: "-", RepoName: "-"} 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 { type statusRecorder struct {
+19 -6
View File
@@ -1,22 +1,35 @@
package rpm package rpm
import "bytes" import codit_logger "codit/logger"
import "net/http" import "net/http"
import "net/http/httptest" import "net/http/httptest"
import "os" import "os"
import "path/filepath" import "path/filepath"
import "testing" 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) { func TestHTTPServerUnauthorizedWithoutBasicAuth(t *testing.T) {
var dir string var dir string
var logger *util.Logger var logger codit_logger.Logger
var server *HTTPServer var server *HTTPServer
var req *http.Request var req *http.Request
var recorder *httptest.ResponseRecorder var recorder *httptest.ResponseRecorder
dir = t.TempDir() dir = t.TempDir()
logger = util.NewLogger("test", &bytes.Buffer{}, util.LOG_ALL) logger = &testLogger{}
defer logger.Close() defer logger.Close()
server = NewHTTPServer(dir, func(username, password string) (bool, error) { server = NewHTTPServer(dir, func(username, password string) (bool, error) {
return true, nil return true, nil
@@ -31,7 +44,7 @@ func TestHTTPServerUnauthorizedWithoutBasicAuth(t *testing.T) {
func TestHTTPServerAuthorized(t *testing.T) { func TestHTTPServerAuthorized(t *testing.T) {
var dir string var dir string
var logger *util.Logger var logger codit_logger.Logger
var server *HTTPServer var server *HTTPServer
var req *http.Request var req *http.Request
var recorder *httptest.ResponseRecorder var recorder *httptest.ResponseRecorder
@@ -39,7 +52,7 @@ func TestHTTPServerAuthorized(t *testing.T) {
dir = t.TempDir() dir = t.TempDir()
path = filepath.Join(dir, "a.txt") path = filepath.Join(dir, "a.txt")
_ = os.WriteFile(path, []byte("ok"), 0o644) _ = os.WriteFile(path, []byte("ok"), 0o644)
logger = util.NewLogger("test", &bytes.Buffer{}, util.LOG_ALL) logger = &testLogger{}
defer logger.Close() defer logger.Close()
server = NewHTTPServer(dir, func(username, password string) (bool, error) { server = NewHTTPServer(dir, func(username, password string) (bool, error) {
return username == "u" && password == "p", nil return username == "u" && password == "p", nil
+29 -29
View File
@@ -1,5 +1,6 @@
package rpm package rpm
import codit_logger "codit/logger"
import "compress/gzip" import "compress/gzip"
import "context" import "context"
import "crypto/md5" import "crypto/md5"
@@ -26,13 +27,12 @@ import "time"
import "codit/internal/db" import "codit/internal/db"
import "codit/internal/models" import "codit/internal/models"
import "codit/internal/util"
const logIDRPMMirror string = "rpm-mirror" const logIDRPMMirror string = "rpm-mirror"
type MirrorManager struct { type MirrorManager struct {
store *db.Store store *db.Store
logger *util.Logger logger codit_logger.Logger
meta *MetaManager meta *MetaManager
stopCh chan struct{} stopCh chan struct{}
cancelMu sync.Mutex cancelMu sync.Mutex
@@ -93,7 +93,7 @@ type mirrorHTTPConfig struct {
DefaultServer string 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 var m *MirrorManager
m = &MirrorManager{ m = &MirrorManager{
store: store, store: store,
@@ -131,7 +131,7 @@ func (m *MirrorManager) Start() {
} }
err = m.store.ResetRunningRPMMirrorTasks() err = m.store.ResetRunningRPMMirrorTasks()
if err != nil && m.logger != nil { 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() tasks, err = m.store.ListRPMMirrorPaths()
if err == nil { if err == nil {
@@ -175,7 +175,7 @@ func (m *MirrorManager) runDue() {
tasks, err = m.store.ListDueRPMMirrorTasks(now, 8) tasks, err = m.store.ListDueRPMMirrorTasks(now, 8)
if err != nil { if err != nil {
if m.logger != 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 return
} }
@@ -183,7 +183,7 @@ func (m *MirrorManager) runDue() {
runID, started, err = m.store.TryStartRPMMirrorRun(tasks[i].RepoID, tasks[i].MirrorPath, now) runID, started, err = m.store.TryStartRPMMirrorRun(tasks[i].RepoID, tasks[i].MirrorPath, now)
if err != nil { if err != nil {
if m.logger != 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 continue
} }
@@ -229,20 +229,20 @@ func (m *MirrorManager) syncOne(task models.RPMMirrorTask, runID string) {
cfg, err = buildMirrorHTTPConfig(task) cfg, err = buildMirrorHTTPConfig(task)
if err != nil { if err != nil {
if m.logger != 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()) m.finishTaskRun(task, runID, false, "start", 0, 0, 0, 0, "", err.Error())
return return
} }
client = buildMirrorHTTPClient(cfg) client = buildMirrorHTTPClient(cfg)
if m.logger != nil { 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) _ = m.store.UpdateRPMMirrorTaskProgress(task.RepoID, task.MirrorPath, "fetch_repodata", 0, 0, 0, 0)
repomdData, err = mirrorFetch(syncCtx, client, cfg, "repodata/repomd.xml") repomdData, err = mirrorFetch(syncCtx, client, cfg, "repodata/repomd.xml")
if err != nil { if err != nil {
if m.logger != 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()) m.finishTaskRun(task, runID, false, "fetch_repodata", 0, 0, 0, 0, "", err.Error())
return return
@@ -253,7 +253,7 @@ func (m *MirrorManager) syncOne(task models.RPMMirrorTask, runID string) {
ensureRepodata(task, localRoot, m.meta, m.logger) ensureRepodata(task, localRoot, m.meta, m.logger)
} }
if m.logger != nil { 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, "") m.finishTaskRun(task, runID, true, "no_change", 0, 0, 0, 0, revision, "")
return return
@@ -261,7 +261,7 @@ func (m *MirrorManager) syncOne(task models.RPMMirrorTask, runID string) {
primaryHref, err = parseRepomdPrimaryHref(repomdData) primaryHref, err = parseRepomdPrimaryHref(repomdData)
if err != nil { if err != nil {
if m.logger != 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()) m.finishTaskRun(task, runID, false, "fetch_repodata", 0, 0, 0, 0, "", err.Error())
return return
@@ -270,7 +270,7 @@ func (m *MirrorManager) syncOne(task models.RPMMirrorTask, runID string) {
primaryData, err = mirrorFetch(syncCtx, client, cfg, primaryHref) primaryData, err = mirrorFetch(syncCtx, client, cfg, primaryHref)
if err != nil { if err != nil {
if m.logger != 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()) m.finishTaskRun(task, runID, false, "fetch_primary", 0, 0, 0, 0, "", err.Error())
return return
@@ -279,7 +279,7 @@ func (m *MirrorManager) syncOne(task models.RPMMirrorTask, runID string) {
primaryData, err = gunzipBytes(primaryData) primaryData, err = gunzipBytes(primaryData)
if err != nil { if err != nil {
if m.logger != 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()) m.finishTaskRun(task, runID, false, "fetch_primary", 0, 0, 0, 0, "", err.Error())
return return
@@ -288,15 +288,15 @@ func (m *MirrorManager) syncOne(task models.RPMMirrorTask, runID string) {
expected, duplicateCount, err = parsePrimaryPackages(primaryData) expected, duplicateCount, err = parsePrimaryPackages(primaryData)
if err != nil { if err != nil {
if m.logger != 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()) m.finishTaskRun(task, runID, false, "fetch_primary", 0, 0, 0, 0, "", err.Error())
return return
} }
if m.logger != nil { 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 { 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) 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) canceled = errors.Is(err, context.Canceled)
if m.logger != nil { if m.logger != nil {
if canceled { 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 { } 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 { if canceled {
@@ -318,14 +318,14 @@ func (m *MirrorManager) syncOne(task models.RPMMirrorTask, runID string) {
} }
if m.meta != nil && changed > 0 { if m.meta != nil && changed > 0 {
if m.logger != nil { 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.meta.Schedule(localRoot)
} }
m.finishTaskRun(task, runID, true, "done", total, done, failed, deleted, revision, "") m.finishTaskRun(task, runID, true, "done", total, done, failed, deleted, revision, "")
_ = m.store.CleanupRPMMirrorRunsRetention(task.RepoID, task.MirrorPath, 200, 30) _ = m.store.CleanupRPMMirrorRunsRetention(task.RepoID, task.MirrorPath, 200, 30)
if m.logger != nil { 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() finishedAt = time.Now().UTC().Unix()
err = m.store.FinishRPMMirrorTaskRun(task.RepoID, task.MirrorPath, runID, success, finishedAt, step, total, done, failed, deleted, revision, errMsg) err = m.store.FinishRPMMirrorTaskRun(task.RepoID, task.MirrorPath, runID, success, finishedAt, step, total, done, failed, deleted, revision, errMsg)
if err != nil && m.logger != nil { 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 continue
} }
if m.logger != nil { 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))) err = os.Remove(filepath.Join(localRoot, filepath.FromSlash(path)))
if err == nil || os.IsNotExist(err) { if err == nil || os.IsNotExist(err) {
@@ -377,7 +377,7 @@ func (m *MirrorManager) applyMirror(ctx context.Context, task models.RPMMirrorTa
changed = changed + 1 changed = changed + 1
} else { } else {
if m.logger != nil { 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 needDownload {
if m.logger != nil { 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) err = mirrorDownload(ctx, client, cfg, path, fullPath, checksum.Algo, checksum.Value)
if err != nil { if err != nil {
failed = failed + 1 failed = failed + 1
if m.logger != nil { 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) _ = m.store.UpdateRPMMirrorTaskProgress(task.RepoID, task.MirrorPath, "apply", total, done, failed, deleted)
continue continue
} }
changed = changed + 1 changed = changed + 1
if m.logger != nil { 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 { } else {
if m.logger != nil { 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 done = done + 1
@@ -853,7 +853,7 @@ func mirrorTaskKey(repoID string, path string) string {
return repoID + "\x00" + path 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 repomdPath string
var statErr error var statErr error
repomdPath = filepath.Join(localRoot, "repodata", "repomd.xml") repomdPath = filepath.Join(localRoot, "repodata", "repomd.xml")
@@ -862,7 +862,7 @@ func ensureRepodata(task models.RPMMirrorTask, localRoot string, meta *MetaManag
return return
} }
if logger != nil { 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) 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 "fmt"
import "io" import "io"
@@ -11,8 +11,8 @@ import "sync/atomic"
import "syscall" import "syscall"
import "time" import "time"
type LogLevel int type LogLevel = int
type LogMask int type LogMask = int
const ( const (
LOG_DEBUG LogLevel = 1 << iota 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_ALL LogMask = LogMask(LOG_DEBUG | LOG_INFO | LOG_WARN | LOG_ERROR)
const LOG_NONE LogMask = LogMask(0) 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 { type logger_msg_t struct {
code int code int
data string data string
} }
type Logger struct { type AsyncLogger struct {
id string id string
out io.Writer out io.Writer
mask LogMask mask LogMask
@@ -63,8 +70,8 @@ func _is_ansi_tty(fd uintptr) bool {
} }
func NewLogger(id string, w io.Writer, mask LogMask) *Logger { func NewAsyncLogger(id string, w io.Writer, mask LogMask) *AsyncLogger {
var l *Logger var l *AsyncLogger
var f *os.File var f *os.File
var ok bool var ok bool
var use_color bool var use_color bool
@@ -73,7 +80,7 @@ func NewLogger(id string, w io.Writer, mask LogMask) *Logger {
f, ok = w.(*os.File) f, ok = w.(*os.File)
if ok { use_color = _is_ansi_tty(f.Fd()) } if ok { use_color = _is_ansi_tty(f.Fd()) }
l = &Logger{ l = &AsyncLogger{
id: id, id: id,
out: w, out: w,
mask: mask, mask: mask,
@@ -86,8 +93,8 @@ func NewLogger(id string, w io.Writer, mask LogMask) *Logger {
return l return l
} }
func NewLoggerToFile(id string, file_name string, max_size int64, rotate int, mask LogMask) (*Logger, error) { func NewAsyncLoggerToFile(id string, file_name string, max_size int64, rotate int, mask LogMask) (*AsyncLogger, error) {
var l *Logger var l *AsyncLogger
var f *os.File var f *os.File
var fi os.FileInfo var fi os.FileInfo
var err error var err error
@@ -102,7 +109,7 @@ func NewLoggerToFile(id string, file_name string, max_size int64, rotate int, ma
rotate = 0 rotate = 0
} }
l = &Logger{ l = &AsyncLogger{
id: id, id: id,
out: f, out: f,
mask: mask, mask: mask,
@@ -119,7 +126,7 @@ func NewLoggerToFile(id string, file_name string, max_size int64, rotate int, ma
return l, nil return l, nil
} }
func (l *Logger) Close() { func (l *AsyncLogger) Close() {
if l.closed.CompareAndSwap(false, true) { if l.closed.CompareAndSwap(false, true) {
l.msg_chan <- logger_msg_t{code: 1} l.msg_chan <- logger_msg_t{code: 1}
l.wg.Wait() 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} l.msg_chan <- logger_msg_t{code: 2}
} }
func (l *Logger) logger_task() { func (l *AsyncLogger) logger_task() {
var msg logger_msg_t var msg logger_msg_t
defer l.wg.Done() 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 } if l.mask & LogMask(level) == 0 { return }
l.write(id, level, 1, fmtstr, args...) 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 } if l.mask & LogMask(level) == 0 { return }
l.write(id, level, call_depth + 1, fmtstr, args...) 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 now time.Time
var off_m int var off_m int
var off_h 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() } l.msg_chan <- logger_msg_t{ code: 0, data: sb.String() }
} }
func (l *Logger) rotate() { func (l *AsyncLogger) rotate() {
var f *os.File var f *os.File
var fi os.FileInfo var fi os.FileInfo
var i int 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 { switch level {
case LOG_ERROR: case LOG_ERROR:
return "\x1B[31m" // red 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`. - 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. - 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 ## Core Identity And Auth
### `users` ### `users`