config.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  1. package config
  2. import (
  3. "fmt"
  4. )
  5. // Action represents the core functionality of OliveTin - commands that show up
  6. // as buttons in the UI.
  7. type Action struct {
  8. ID string `koanf:"id"`
  9. Title string `koanf:"title"`
  10. Icon string `koanf:"icon"`
  11. Shell string `koanf:"shell"`
  12. Exec []string `koanf:"exec"`
  13. ShellAfterCompleted string `koanf:"shellAfterCompleted"`
  14. Timeout int `koanf:"timeout"`
  15. Acls []string `koanf:"acls"`
  16. Entity string `koanf:"entity"`
  17. Hidden bool `koanf:"hidden"`
  18. ExecOnStartup bool `koanf:"execOnStartup"`
  19. ExecOnCron []string `koanf:"execOnCron"`
  20. ExecOnFileCreatedInDir []string `koanf:"execOnFileCreatedInDir"`
  21. ExecOnFileChangedInDir []string `koanf:"execOnFileChangedInDir"`
  22. ExecOnCalendarFile string `koanf:"execOnCalendarFile"`
  23. ExecOnWebhook []WebhookConfig `koanf:"execOnWebhook"`
  24. Triggers []string `koanf:"triggers"`
  25. MaxConcurrent int `koanf:"maxConcurrent"`
  26. MaxRate []RateSpec `koanf:"maxRate"`
  27. Arguments []ActionArgument `koanf:"arguments"`
  28. PopupOnStart string `koanf:"popupOnStart"`
  29. SaveLogs SaveLogsConfig `koanf:"saveLogs"`
  30. EnabledExpression string `koanf:"enabledExpression"`
  31. }
  32. // ActionArgument objects appear on Actions.
  33. type ActionArgument struct {
  34. Name string `koanf:"name"`
  35. Title string `koanf:"title"`
  36. Description string `koanf:"description"`
  37. Type string `koanf:"type"`
  38. Default string `koanf:"default"`
  39. Choices []ActionArgumentChoice `koanf:"choices"`
  40. Entity string `koanf:"entity"`
  41. RejectNull bool `koanf:"rejectNull"`
  42. Suggestions map[string]string `koanf:"suggestions"`
  43. SuggestionsBrowserKey string `koanf:"suggestionsBrowserKey"`
  44. MaxUploadBytes HumanReadableBytes `koanf:"maxUploadBytes"`
  45. AllowedMimeTypes []string `koanf:"allowedMimeTypes"`
  46. }
  47. // ActionArgumentChoice represents a predefined choice for an argument.
  48. type ActionArgumentChoice struct {
  49. Value string `koanf:"value"`
  50. Title string `koanf:"title"`
  51. }
  52. // RateSpec allows you to set a max frequency for an action.
  53. type RateSpec struct {
  54. Limit int `koanf:"limit"`
  55. Duration string `koanf:"duration"`
  56. }
  57. // WebhookConfig defines configuration for generic webhook triggers.
  58. type WebhookConfig struct {
  59. Secret string `koanf:"secret"` // Optional: secret for signature verification
  60. AuthType string `koanf:"authType"` // Optional: "hmac-sha256", "hmac-sha1", "bearer", "basic", "none"
  61. AuthHeader string `koanf:"authHeader"` // Optional: custom header name for auth (default: "X-Webhook-Signature")
  62. MatchHeaders map[string]string `koanf:"matchHeaders"` // Match HTTP headers
  63. MatchPath string `koanf:"matchPath"` // JSONPath expression to match in request body (format: "jsonpath=value" or just "jsonpath")
  64. MatchQuery map[string]string `koanf:"matchQuery"` // Match URL query parameters
  65. Extract map[string]string `koanf:"extract"` // Map action argument names to JSONPath expressions
  66. Template string `koanf:"template"` // Optional: template name (e.g., "github-push", "github-pr")
  67. }
  68. // Entity represents a "thing" that can have multiple actions associated with it.
  69. // for example, a media player with a start and stop action.
  70. type EntityFile struct {
  71. File string `koanf:"file"`
  72. Name string `koanf:"name"`
  73. Icon string `koanf:"icon"`
  74. }
  75. // PermissionsList defines what users can do with an action.
  76. type PermissionsList struct {
  77. View bool `koanf:"view"`
  78. Exec bool `koanf:"exec"`
  79. Logs bool `koanf:"logs"`
  80. Kill bool `koanf:"kill"`
  81. }
  82. // AccessControlList defines what permissions apply to a user or user group.
  83. type AccessControlList struct {
  84. Name string `koanf:"name"`
  85. AddToEveryAction bool `koanf:"addToEveryAction"`
  86. MatchUsergroups []string `koanf:"matchUsergroups"`
  87. MatchUsernames []string `koanf:"matchUsernames"`
  88. Permissions PermissionsList `koanf:"permissions"`
  89. Policy ConfigurationPolicy `koanf:"policy"`
  90. }
  91. // ConfigurationPolicy defines global settings which are overridden with an ACL.
  92. type ConfigurationPolicy struct {
  93. ShowDiagnostics bool `koanf:"showDiagnostics"`
  94. ShowLogList bool `koanf:"showLogList"`
  95. ShowVersionNumber bool `koanf:"showVersionNumber"`
  96. }
  97. type PrometheusConfig struct {
  98. Enabled bool `koanf:"enabled"`
  99. DefaultGoMetrics bool `koanf:"defaultGoMetrics"`
  100. }
  101. // SecurityConfig allows users to fine tune the security related HTTP headers and cookie options.
  102. type SecurityConfig struct {
  103. HeaderContentSecurityPolicy bool `koanf:"headerContentSecurityPolicy"`
  104. ContentSecurityPolicy string `koanf:"contentSecurityPolicy"`
  105. HeaderXContentTypeOptions bool `koanf:"headerXContentTypeOptions"`
  106. HeaderXFrameOptions bool `koanf:"headerXFrameOptions"`
  107. XFrameOptions string `koanf:"xFrameOptions"`
  108. ForceSecureCookies bool `koanf:"forceSecureCookies"`
  109. }
  110. // Config is the global config used through the whole app.
  111. type Config struct {
  112. UseSingleHTTPFrontend bool `koanf:"useSingleHTTPFrontend"`
  113. ThemeName string `koanf:"themeName"`
  114. ThemeCacheDisabled bool `koanf:"themeCacheDisabled"`
  115. ListenAddressSingleHTTPFrontend string `koanf:"listenAddressSingleHTTPFrontend"`
  116. ListenAddressWebUI string `koanf:"listenAddressWebUI"`
  117. ListenAddressRestActions string `koanf:"listenAddressRestActions"`
  118. ListenAddressPrometheus string `koanf:"listenAddressPrometheus"`
  119. ExternalRestAddress string `koanf:"externalRestAddress"`
  120. LogLevel string `koanf:"logLevel"`
  121. LogDebugOptions LogDebugOptions `koanf:"logDebugOptions"`
  122. LogHistoryPageSize int64 `koanf:"logHistoryPageSize"`
  123. Actions []*Action `koanf:"actions"`
  124. Entities []*EntityFile `koanf:"entities"`
  125. Dashboards []*DashboardComponent `koanf:"dashboards"`
  126. CheckForUpdates bool `koanf:"checkForUpdates"`
  127. PageTitle string `koanf:"pageTitle"`
  128. ShowFooter bool `koanf:"showFooter"`
  129. ShowNavigation bool `koanf:"showNavigation"`
  130. ShowNewVersions bool `koanf:"showNewVersions"`
  131. ShowNavigateOnStartIcons bool `koanf:"showNavigateOnStartIcons"`
  132. EnableCustomJs bool `koanf:"enableCustomJs"`
  133. AuthJwtCookieName string `koanf:"authJwtCookieName"`
  134. AuthJwtHeader string `koanf:"authJwtHeader"`
  135. AuthJwtAud string `koanf:"authJwtAud"`
  136. AuthJwtDomain string `koanf:"authJwtDomain"`
  137. AuthJwtCertsURL string `koanf:"authJwtCertsUrl"`
  138. AuthJwtHmacSecret string `koanf:"authJwtHmacSecret"` // mutually exclusive with pub key config fields
  139. AuthJwtClaimUsername string `koanf:"authJwtClaimUsername"`
  140. AuthJwtClaimUserGroup string `koanf:"authJwtClaimUserGroup"`
  141. AuthJwtPubKeyPath string `koanf:"authJwtPubKeyPath"` // will read pub key from file on disk
  142. AuthHttpHeaderUsername string `koanf:"authHttpHeaderUsername"`
  143. AuthHttpHeaderUserGroup string `koanf:"authHttpHeaderUserGroup"`
  144. AuthHttpHeaderUserGroupSep string `koanf:"authHttpHeaderUserGroupSep"`
  145. AuthLocalUsers AuthLocalUsersConfig `koanf:"authLocalUsers"`
  146. AuthLoginUrl string `koanf:"authLoginUrl"`
  147. AuthRequireGuestsToLogin bool `koanf:"authRequireGuestsToLogin"`
  148. AuthOAuth2RedirectURL string `koanf:"authOAuth2RedirectUrl"`
  149. AuthOAuth2Providers map[string]*OAuth2Provider `koanf:"authOAuth2Providers"`
  150. DefaultPermissions PermissionsList `koanf:"defaultPermissions"`
  151. DefaultPolicy ConfigurationPolicy `koanf:"defaultPolicy"`
  152. AccessControlLists []*AccessControlList `koanf:"accessControlLists"`
  153. WebUIDir string `koanf:"webUIDir"`
  154. CronSupportForSeconds bool `koanf:"cronSupportForSeconds"`
  155. SectionNavigationStyle string `koanf:"sectionNavigationStyle"`
  156. DefaultPopupOnStart string `koanf:"defaultPopupOnStart"`
  157. InsecureAllowDumpOAuth2UserData bool `koanf:"insecureAllowDumpOAuth2UserData"`
  158. InsecureAllowDumpVars bool `koanf:"insecureAllowDumpVars"`
  159. InsecureAllowDumpSos bool `koanf:"insecureAllowDumpSos"`
  160. InsecureAllowDumpActionMap bool `koanf:"insecureAllowDumpActionMap"`
  161. InsecureAllowDumpJwtClaims bool `koanf:"insecureAllowDumpJwtClaims"`
  162. Prometheus PrometheusConfig `koanf:"prometheus"`
  163. Security SecurityConfig `koanf:"security"`
  164. SaveLogs SaveLogsConfig `koanf:"saveLogs"`
  165. DefaultIconForActions string `koanf:"defaultIconForActions"`
  166. DefaultIconForDirectories string `koanf:"defaultIconForDirectories"`
  167. DefaultIconForBack string `koanf:"defaultIconForBack"`
  168. AdditionalNavigationLinks []*NavigationLink `koanf:"additionalNavigationLinks"`
  169. ServiceHostMode string `koanf:"serviceHostMode"`
  170. StyleMods []string `koanf:"styleMods"`
  171. BannerMessage string `koanf:"bannerMessage"`
  172. BannerCSS string `koanf:"bannerCss"`
  173. Include string `koanf:"include"`
  174. FileUploads FileUploadsConfig `koanf:"fileUploads"`
  175. sourceFiles []string
  176. }
  177. // FileUploadsConfig controls server-side staging of uploaded action argument files.
  178. type FileUploadsConfig struct {
  179. TempDirectory string `koanf:"tempDirectory"`
  180. MaxBytes HumanReadableBytes `koanf:"maxBytes"`
  181. TokenTTLSeconds int `koanf:"tokenTTLSeconds"`
  182. DefaultAllowedMimeTypes []string `koanf:"defaultAllowedMimeTypes"`
  183. }
  184. type AuthLocalUsersConfig struct {
  185. Enabled bool `koanf:"enabled"`
  186. Users []*LocalUser `koanf:"users"`
  187. }
  188. type LocalUser struct {
  189. Username string `koanf:"username"`
  190. Usergroup string `koanf:"usergroup"`
  191. Password string `koanf:"password"`
  192. }
  193. type OAuth2Provider struct {
  194. Name string `koanf:"name"`
  195. Title string `koanf:"title"`
  196. ClientID string `koanf:"clientId"`
  197. ClientSecret string `koanf:"clientSecret"`
  198. Icon string `koanf:"icon"`
  199. Scopes []string `koanf:"scopes"`
  200. AuthUrl string `koanf:"authUrl"`
  201. TokenUrl string `koanf:"tokenUrl"`
  202. WhoamiUrl string `koanf:"whoamiUrl"`
  203. UsernameField string `koanf:"usernameField"`
  204. UserGroupField string `koanf:"userGroupField"`
  205. InsecureSkipVerify bool `koanf:"insecureSkipVerify"`
  206. CallbackTimeout int `koanf:"callbackTimeout"`
  207. CertBundlePath string `koanf:"certBundlePath"`
  208. AddToUsergroup string `koanf:"addToUsergroup"`
  209. }
  210. type NavigationLink struct {
  211. Title string `koanf:"title"`
  212. Url string `koanf:"url"`
  213. Target string `koanf:"target"`
  214. }
  215. type SaveLogsConfig struct {
  216. ResultsDirectory string `koanf:"resultsDirectory"`
  217. OutputDirectory string `koanf:"outputDirectory"`
  218. }
  219. type LogDebugOptions struct {
  220. SingleFrontendRequests bool `koanf:"singleFrontendRequests"`
  221. SingleFrontendRequestHeaders bool `koanf:"singleFrontendRequestHeaders"`
  222. AclCheckStarted bool `koanf:"aclCheckStarted"`
  223. AclMatched bool `koanf:"aclMatched"`
  224. AclNotMatched bool `koanf:"aclNotMatched"`
  225. AclNoneMatched bool `koanf:"aclNoneMatched"`
  226. }
  227. type DashboardComponent struct {
  228. Title string `koanf:"title"`
  229. Type string `koanf:"type"`
  230. Entity string `koanf:"entity"`
  231. Icon string `koanf:"icon"`
  232. CssClass string `koanf:"cssClass"`
  233. InlineAction *Action `koanf:"inlineAction"`
  234. Contents []*DashboardComponent `koanf:"contents"`
  235. }
  236. func DefaultConfig() *Config {
  237. return DefaultConfigWithBasePort(1337)
  238. }
  239. // DefaultConfig gets a new Config structure with sensible default values.
  240. func DefaultConfigWithBasePort(basePort int) *Config {
  241. config := Config{}
  242. config.UseSingleHTTPFrontend = true
  243. config.PageTitle = "OliveTin"
  244. config.ShowFooter = true
  245. config.ShowNavigation = true
  246. config.ShowNewVersions = true
  247. config.ShowNavigateOnStartIcons = true
  248. config.EnableCustomJs = false
  249. config.ExternalRestAddress = "."
  250. config.LogLevel = "INFO"
  251. config.LogHistoryPageSize = 10
  252. config.CheckForUpdates = false
  253. config.DefaultPermissions.Exec = true
  254. config.DefaultPermissions.View = true
  255. config.DefaultPermissions.Logs = true
  256. config.DefaultPermissions.Kill = true
  257. config.AuthJwtClaimUsername = "name"
  258. config.AuthJwtClaimUserGroup = "group"
  259. config.AuthRequireGuestsToLogin = false
  260. config.WebUIDir = "./webui"
  261. config.CronSupportForSeconds = false
  262. config.SectionNavigationStyle = "sidebar"
  263. config.DefaultPopupOnStart = "nothing"
  264. config.InsecureAllowDumpVars = false
  265. config.InsecureAllowDumpSos = false
  266. config.InsecureAllowDumpActionMap = false
  267. config.InsecureAllowDumpJwtClaims = false
  268. config.Prometheus.Enabled = false
  269. config.Prometheus.DefaultGoMetrics = false
  270. config.Security.HeaderContentSecurityPolicy = true
  271. config.Security.ContentSecurityPolicy = ContentSecurityPolicyDefault
  272. config.Security.HeaderXContentTypeOptions = true
  273. config.Security.HeaderXFrameOptions = true
  274. config.Security.XFrameOptions = "DENY"
  275. config.DefaultIconForActions = "😀"
  276. config.DefaultIconForDirectories = "&#128193"
  277. config.DefaultIconForBack = "«"
  278. config.ThemeCacheDisabled = false
  279. config.ServiceHostMode = ""
  280. config.ListenAddressSingleHTTPFrontend = fmt.Sprintf("0.0.0.0:%d", basePort)
  281. config.ListenAddressRestActions = fmt.Sprintf("localhost:%d", basePort+1)
  282. config.ListenAddressWebUI = fmt.Sprintf("localhost:%d", basePort+3)
  283. config.ListenAddressPrometheus = fmt.Sprintf("localhost:%d", basePort+4)
  284. config.DefaultPolicy.ShowDiagnostics = true
  285. config.DefaultPolicy.ShowLogList = true
  286. config.DefaultPolicy.ShowVersionNumber = true
  287. config.FileUploads.MaxBytes = DefaultFileUploadMaxBytes
  288. config.FileUploads.TokenTTLSeconds = DefaultFileUploadTokenTTLSeconds
  289. return &config
  290. }
  291. const (
  292. // DefaultFileUploadMaxBytes is 10 MB (SI), matching humanize.ParseBytes("10 MB").
  293. DefaultFileUploadMaxBytes HumanReadableBytes = 10_000_000
  294. DefaultFileUploadTokenTTLSeconds = 300
  295. )
  296. // EffectiveFileUploadMaxBytes returns the maximum upload size for an action argument.
  297. func (a *ActionArgument) EffectiveFileUploadMaxBytes(cfg *Config) int64 {
  298. if a.MaxUploadBytes > 0 {
  299. return int64(a.MaxUploadBytes)
  300. }
  301. if cfg != nil && cfg.FileUploads.MaxBytes > 0 {
  302. return int64(cfg.FileUploads.MaxBytes)
  303. }
  304. return int64(DefaultFileUploadMaxBytes)
  305. }
  306. // EffectiveFileUploadAllowedMimeTypes returns allowed MIME types (empty means none allowed).
  307. func (a *ActionArgument) EffectiveFileUploadAllowedMimeTypes(cfg *Config) []string {
  308. if len(a.AllowedMimeTypes) > 0 {
  309. return a.AllowedMimeTypes
  310. }
  311. if cfg != nil && len(cfg.FileUploads.DefaultAllowedMimeTypes) > 0 {
  312. return cfg.FileUploads.DefaultAllowedMimeTypes
  313. }
  314. return nil
  315. }