ci: add dedicated authelia-gen command (#3463)

Adds a dedicated authelia code/doc gen command.
pull/3518/head
James Elliott 2022-06-14 22:40:00 +10:00 committed by GitHub
parent 802b21f3b5
commit 5304178165
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
44 changed files with 1798 additions and 870 deletions

View File

@ -0,0 +1,34 @@
package main
import (
"github.com/spf13/cobra"
)
func newAllCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "all",
Short: "Run all generators with default options",
RunE: allRunE,
}
return cmd
}
func allRunE(cmd *cobra.Command, args []string) (err error) {
for _, subCmd := range cmd.Parent().Commands() {
if subCmd == cmd || subCmd.Use == "completion" || subCmd.Use == "help [command]" {
continue
}
switch {
case subCmd.RunE != nil:
if err = subCmd.RunE(subCmd, args); err != nil {
return err
}
case subCmd.Run != nil:
subCmd.Run(subCmd, args)
}
}
return nil
}

View File

@ -0,0 +1,32 @@
package main
import (
"github.com/spf13/cobra"
)
func newCodeCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "code",
Short: "Generate code",
RunE: codeRunE,
}
cmd.AddCommand(newCodeKeysCmd())
return cmd
}
func codeRunE(cmd *cobra.Command, args []string) (err error) {
for _, subCmd := range cmd.Commands() {
switch {
case subCmd.RunE != nil:
if err = subCmd.RunE(subCmd, args); err != nil {
return err
}
case subCmd.Run != nil:
subCmd.Run(subCmd, args)
}
}
return nil
}

View File

@ -16,92 +16,63 @@ import (
"github.com/authelia/authelia/v4/internal/configuration/schema"
)
// NewRunGenCmd implements the code generation cobra command.
func NewRunGenCmd() (cmd *cobra.Command) {
cmd = &cobra.Command{
Use: "gen",
RunE: runGenE,
func newCodeKeysCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "keys",
Short: "Generate the list of valid configuration keys",
RunE: codeKeysRunE,
}
cmd.Flags().StringP("file", "f", "./internal/configuration/schema/keys.go", "Sets the path of the keys file")
cmd.Flags().String("package", "schema", "Sets the package name of the keys file")
return cmd
}
func runGenE(cmd *cobra.Command, args []string) (err error) {
if err = genConfigurationKeys(); err != nil {
func codeKeysRunE(cmd *cobra.Command, args []string) (err error) {
var (
file string
f *os.File
)
data := keysTemplateStruct{
Timestamp: time.Now(),
Keys: readTags("", reflect.TypeOf(schema.Configuration{})),
}
if file, err = cmd.Flags().GetString("file"); err != nil {
return err
}
return nil
}
func genConfigurationKeys() (err error) {
data := loadKeysTemplate()
f, err := os.Create("./internal/configuration/schema/keys.go")
if err != nil {
if data.Package, err = cmd.Flags().GetString("package"); err != nil {
return err
}
return keysTemplate.Execute(f, data)
if f, err = os.Create(file); err != nil {
return fmt.Errorf("failed to create file '%s': %w", file, err)
}
var (
content []byte
tmpl *template.Template
)
if content, err = templatesFS.ReadFile("templates/config_keys.go.tmpl"); err != nil {
return err
}
if tmpl, err = template.New("keys").Parse(string(content)); err != nil {
return err
}
return tmpl.Execute(f, data)
}
var keysTemplate = template.Must(template.New("keys").Parse(`// Code generated by go generate. DO NOT EDIT.
//
// Run the following command to generate this file:
// go run ./cmd/authelia-scripts gen
//
package schema
// Keys represents the detected schema keys.
var Keys = []string{
{{- range .Keys }}
{{ printf "%q" . }},
{{- end }}
}
`))
type keysTemplateStruct struct {
Timestamp time.Time
Keys []string
}
func loadKeysTemplate() keysTemplateStruct {
config := schema.Configuration{
Storage: schema.StorageConfiguration{
Local: &schema.LocalStorageConfiguration{},
MySQL: &schema.MySQLStorageConfiguration{},
PostgreSQL: &schema.PostgreSQLStorageConfiguration{},
},
Notifier: schema.NotifierConfiguration{
FileSystem: &schema.FileSystemNotifierConfiguration{},
SMTP: &schema.SMTPNotifierConfiguration{
TLS: &schema.TLSConfig{},
},
},
AuthenticationBackend: schema.AuthenticationBackendConfiguration{
File: &schema.FileAuthenticationBackendConfiguration{
Password: &schema.PasswordConfiguration{},
},
LDAP: &schema.LDAPAuthenticationBackendConfiguration{
TLS: &schema.TLSConfig{},
},
},
Session: schema.SessionConfiguration{
Redis: &schema.RedisSessionConfiguration{
TLS: &schema.TLSConfig{},
HighAvailability: &schema.RedisHighAvailabilityConfiguration{},
},
},
IdentityProviders: schema.IdentityProvidersConfiguration{
OIDC: &schema.OpenIDConnectConfiguration{},
},
}
return keysTemplateStruct{
Timestamp: time.Now(),
Keys: readTags("", reflect.TypeOf(config)),
}
Package string
}
var decodedTypes = []reflect.Type{

View File

@ -0,0 +1,33 @@
package main
import (
"github.com/spf13/cobra"
)
func newDocsCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "docs",
Short: "Generate docs",
RunE: docsRunE,
}
cmd.PersistentFlags().StringP("cwd", "C", "", "Sets the CWD for git commands")
cmd.AddCommand(newDocsCLICmd(), newDocsDateCmd())
return cmd
}
func docsRunE(cmd *cobra.Command, args []string) (err error) {
for _, subCmd := range cmd.Commands() {
switch {
case subCmd.RunE != nil:
if err = subCmd.RunE(subCmd, args); err != nil {
return err
}
case subCmd.Run != nil:
subCmd.Run(subCmd, args)
}
}
return nil
}

View File

@ -0,0 +1,154 @@
package main
import (
"fmt"
"os"
"path/filepath"
"strings"
"time"
"github.com/spf13/cobra"
"github.com/spf13/cobra/doc"
cmdscripts "github.com/authelia/authelia/v4/cmd/authelia-scripts/cmd"
"github.com/authelia/authelia/v4/internal/commands"
)
func newDocsCLICmd() *cobra.Command {
cmd := &cobra.Command{
Use: "cli",
Short: "Generate CLI docs",
RunE: docsCLIRunE,
}
cmd.Flags().StringP("directory", "d", "./docs/content/en/reference/cli", "The directory to store the markdown in")
return cmd
}
func docsCLIRunE(cmd *cobra.Command, args []string) (err error) {
var root string
if root, err = cmd.Flags().GetString("directory"); err != nil {
return err
}
if err = os.MkdirAll(root, 0775); err != nil {
if !os.IsExist(err) {
return err
}
}
if err = genCLIDoc(commands.NewRootCmd(), filepath.Join(root, "authelia")); err != nil {
return err
}
if err = genCLIDocWriteIndex(root, "authelia"); err != nil {
return err
}
if err = genCLIDoc(cmdscripts.NewRootCmd(), filepath.Join(root, "authelia-scripts")); err != nil {
return err
}
if err = genCLIDocWriteIndex(root, "authelia-scripts"); err != nil {
return err
}
if err = genCLIDoc(newRootCmd(), filepath.Join(root, "authelia-gen")); err != nil {
return err
}
if err = genCLIDocWriteIndex(root, "authelia-gen"); err != nil {
return err
}
return nil
}
func genCLIDoc(cmd *cobra.Command, path string) (err error) {
if err = os.Mkdir(path, 0755); err != nil {
if !os.IsExist(err) {
return err
}
}
if err = doc.GenMarkdownTreeCustom(cmd, path, prepend, linker); err != nil {
return err
}
return nil
}
func genCLIDocWriteIndex(path, name string) (err error) {
now := time.Now()
f, err := os.Create(filepath.Join(path, name, "_index.md"))
if err != nil {
return err
}
weight := 900
if name == "authelia" {
weight = 320
}
_, err = fmt.Fprintf(f, indexDocs, name, now.Format(dateFmtYAML), "cli-"+name, weight)
return err
}
func prepend(input string) string {
now := time.Now()
pathz := strings.Split(strings.Replace(input, ".md", "", 1), "\\")
parts := strings.Split(pathz[len(pathz)-1], "_")
cmd := parts[0]
args := strings.Join(parts, " ")
weight := 330
if len(parts) == 1 {
weight = 320
}
return fmt.Sprintf(prefixDocs, args, fmt.Sprintf("Reference for the %s command.", args), "", now.Format(dateFmtYAML), "cli-"+cmd, weight)
}
func linker(input string) string {
return input
}
const indexDocs = `---
title: "%s"
description: ""
lead: ""
date: %s
draft: false
images: []
menu:
reference:
parent: "cli"
identifier: "%s"
weight: %d
toc: true
---
`
const prefixDocs = `---
title: "%s"
description: "%s"
lead: "%s"
date: %s
draft: false
images: []
menu:
reference:
parent: "%s"
weight: %d
toc: true
---
`

View File

@ -0,0 +1,220 @@
package main
import (
"bufio"
"bytes"
"fmt"
"io/fs"
"os"
"os/exec"
"path/filepath"
"strings"
"time"
"github.com/spf13/cobra"
"gopkg.in/yaml.v3"
)
func newDocsDateCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "date",
Short: "Generate doc dates",
RunE: docsDateRunE,
}
cmd.Flags().StringP("directory", "d", "./docs/content", "The directory to modify")
cmd.Flags().String("commit-until", "HEAD", "The commit to check the logs until")
cmd.Flags().String("commit-since", "", "The commit to check the logs since")
return cmd
}
func docsDateRunE(cmd *cobra.Command, args []string) (err error) {
var (
dir, cwd, commitUtil, commitSince, commitFilter string
)
if dir, err = cmd.Flags().GetString("directory"); err != nil {
return err
}
if cwd, err = cmd.Flags().GetString("cwd"); err != nil {
return err
}
if cmd.Flags().Changed("commit-since") {
if commitUtil, err = cmd.Flags().GetString("commit-util"); err != nil {
return err
}
if commitSince, err = cmd.Flags().GetString("commit-since"); err != nil {
return err
}
commitFilter = fmt.Sprintf("%s...%s", commitUtil, commitSince)
}
return filepath.Walk(dir, func(path string, info fs.FileInfo, err error) error {
if err != nil {
return err
}
if !strings.HasSuffix(info.Name(), ".md") {
return nil
}
abs, err := filepath.Abs(path)
if err != nil {
return nil
}
frontmatterBytes := getFrontmatter(abs)
if frontmatterBytes == nil {
return nil
}
frontmatter := map[string]interface{}{}
if err = yaml.Unmarshal(frontmatterBytes, frontmatter); err != nil {
return err
}
var (
date time.Time
)
if value, ok := frontmatter["date"]; ok {
date = value.(time.Time)
}
dateGit := getDateFromGit(cwd, abs, commitFilter)
replaceDates(abs, date, dateGit)
return nil
})
}
var newline = []byte("\n")
func getDateFromGit(cwd, path, commitFilter string) *time.Time {
var args []string
if len(cwd) != 0 {
args = append(args, "-C", cwd)
}
args = append(args, "log")
if len(commitFilter) != 0 {
args = append(args, commitFilter)
}
args = append(args, "-1", "--follow", "--diff-filter=A", "--pretty=format:%cD", "--", path)
return getTimeFromGitCmd(exec.Command("git", args...))
}
func getTimeFromGitCmd(cmd *exec.Cmd) *time.Time {
var (
output []byte
err error
t time.Time
)
if output, err = cmd.Output(); err != nil {
return nil
}
if t, err = time.Parse(dateFmtRFC2822, string(output)); err != nil {
return nil
}
return &t
}
func replaceDates(path string, date time.Time, dateGit *time.Time) {
f, err := os.Open(path)
if err != nil {
return
}
buf := bytes.Buffer{}
scanner := bufio.NewScanner(f)
var dateGitLine string
dateLine := fmt.Sprintf("date: %s", date.Format(dateFmtYAML))
if dateGit != nil {
dateGitLine = fmt.Sprintf("date: %s", dateGit.Format(dateFmtYAML))
} else {
dateGitLine = dateLine
}
found := 0
frontmatter := 0
for scanner.Scan() {
if found < 2 && frontmatter < 2 {
switch {
case scanner.Text() == frontmatterDelimiterLine:
buf.Write(scanner.Bytes())
frontmatter++
case frontmatter != 0 && strings.HasPrefix(scanner.Text(), "date: "):
buf.WriteString(dateGitLine)
found++
default:
buf.Write(scanner.Bytes())
}
} else {
buf.Write(scanner.Bytes())
}
buf.Write(newline)
}
f.Close()
newF, err := os.Create(path)
if err != nil {
return
}
_, _ = buf.WriteTo(newF)
newF.Close()
}
func getFrontmatter(path string) []byte {
f, err := os.Open(path)
if err != nil {
return nil
}
defer f.Close()
scanner := bufio.NewScanner(f)
var start bool
buf := bytes.Buffer{}
for scanner.Scan() {
if start {
if scanner.Text() == frontmatterDelimiterLine {
break
}
buf.Write(scanner.Bytes())
buf.Write(newline)
} else if scanner.Text() == frontmatterDelimiterLine {
start = true
}
}
return buf.Bytes()
}

View File

@ -0,0 +1,7 @@
package main
const (
dateFmtRFC2822 = "Mon, _2 Jan 2006 15:04:05 -0700"
dateFmtYAML = "2006-01-02T15:04:05-07:00"
frontmatterDelimiterLine = "---"
)

View File

@ -0,0 +1,27 @@
package main
import (
"embed"
"github.com/spf13/cobra"
)
//go:embed templates/*
var templatesFS embed.FS
func main() {
if err := newRootCmd().Execute(); err != nil {
panic(err)
}
}
func newRootCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "authelia-gen",
Short: "Authelia's generator tooling",
}
cmd.AddCommand(newAllCmd(), newCodeCmd(), newDocsCmd())
return cmd
}

View File

@ -0,0 +1,14 @@
// Code generated by go generate. DO NOT EDIT.
//
// Run the following command to generate this file:
// go run ./cmd/authelia-gen code keys
//
package {{ .Package }}
// Keys is a list of valid schema keys detected by reflecting over a schema.Configuration struct.
var Keys = []string{
{{- range .Keys }}
{{ printf "%q" . }},
{{- end }}
}

View File

@ -1,4 +1,4 @@
package main
package cmd
import (
"fmt"
@ -12,10 +12,53 @@ import (
"github.com/authelia/authelia/v4/internal/utils"
)
// HostEntry represents an entry in /etc/hosts.
type HostEntry struct {
Domain string
IP string
func newBootstrapCmd() (cmd *cobra.Command) {
cmd = &cobra.Command{
Use: "bootstrap",
Short: cmdBootstrapShort,
Long: cmdBootstrapLong,
Example: cmdBootstrapExample,
Args: cobra.NoArgs,
Run: cmdBootstrapRun,
}
return cmd
}
func cmdBootstrapRun(_ *cobra.Command, _ []string) {
bootstrapPrintln("Checking command installation...")
checkCommandExist("node", "Follow installation guidelines from https://nodejs.org/en/download/package-manager/ or download installer from https://nodejs.org/en/download/")
checkCommandExist("pnpm", "Follow installation guidelines from https://pnpm.io/installation")
checkCommandExist("docker", "Follow installation guidelines from https://docs.docker.com/get-docker/")
checkCommandExist("docker-compose", "Follow installation guidelines from https://docs.docker.com/compose/install/")
bootstrapPrintln("Getting versions of tools")
readVersions()
bootstrapPrintln("Checking if GOPATH is set")
goPathFound := false
for _, v := range os.Environ() {
if strings.HasPrefix(v, "GOPATH=") {
goPathFound = true
break
}
}
if !goPathFound {
log.Fatal("GOPATH is not set")
}
createTemporaryDirectory()
createPNPMDirectory()
bootstrapPrintln("Preparing /etc/hosts to serve subdomains of example.com...")
prepareHostsFile()
fmt.Println()
bootstrapPrintln("Run 'authelia-scripts suites setup Standalone' to start Authelia and visit https://home.example.com:8080.")
bootstrapPrintln("More details at https://github.com/authelia/authelia/blob/master/docs/getting-started.md")
}
var hostEntries = []HostEntry{
@ -78,9 +121,8 @@ func runCommand(cmd string, args ...string) {
func checkCommandExist(cmd string, resolutionHint string) {
fmt.Print("Checking if '" + cmd + "' command is installed...")
command := exec.Command("bash", "-c", "command -v "+cmd) //nolint:gosec // Used only in development.
err := command.Run()
if err != nil {
if command.Run() != nil {
msg := "[ERROR] You must install " + cmd + " on your machine."
if resolutionHint != "" {
msg += fmt.Sprintf(" %s", resolutionHint)
@ -214,40 +256,3 @@ func readVersions() {
readVersion("docker", "--version")
readVersion("docker-compose", "version")
}
// Bootstrap bootstrap authelia dev environment.
func Bootstrap(cobraCmd *cobra.Command, args []string) {
bootstrapPrintln("Checking command installation...")
checkCommandExist("node", "Follow installation guidelines from https://nodejs.org/en/download/package-manager/ or download installer from https://nodejs.org/en/download/")
checkCommandExist("pnpm", "Follow installation guidelines from https://pnpm.io/installation")
checkCommandExist("docker", "Follow installation guidelines from https://docs.docker.com/get-docker/")
checkCommandExist("docker-compose", "Follow installation guidelines from https://docs.docker.com/compose/install/")
bootstrapPrintln("Getting versions of tools")
readVersions()
bootstrapPrintln("Checking if GOPATH is set")
goPathFound := false
for _, v := range os.Environ() {
if strings.HasPrefix(v, "GOPATH=") {
goPathFound = true
break
}
}
if !goPathFound {
log.Fatal("GOPATH is not set")
}
createTemporaryDirectory()
createPNPMDirectory()
bootstrapPrintln("Preparing /etc/hosts to serve subdomains of example.com...")
prepareHostsFile()
fmt.Println()
bootstrapPrintln("Run 'authelia-scripts suites setup Standalone' to start Authelia and visit https://home.example.com:8080.")
bootstrapPrintln("More details at https://github.com/authelia/authelia/blob/master/docs/getting-started.md")
}

View File

@ -1,4 +1,4 @@
package main
package cmd
import (
"os"
@ -12,8 +12,65 @@ import (
"github.com/authelia/authelia/v4/internal/utils"
)
func buildAutheliaBinary(xflags []string, buildkite bool) {
func newBuildCmd() (cmd *cobra.Command) {
cmd = &cobra.Command{
Use: "build",
Short: cmdBuildShort,
Long: cmdBuildLong,
Example: cmdBuildExample,
Args: cobra.NoArgs,
Run: cmdBuildRun,
}
return cmd
}
func cmdBuildRun(cobraCmd *cobra.Command, args []string) {
branch := os.Getenv("BUILDKITE_BRANCH")
if strings.HasPrefix(branch, "renovate/") {
buildFrontend(branch)
log.Info("Skip building Authelia for deps...")
os.Exit(0)
}
log.Info("Building Authelia...")
cmdCleanRun(cobraCmd, args)
xflags, err := getXFlags(branch, os.Getenv("BUILDKITE_BUILD_NUMBER"), "")
if err != nil {
log.Fatal(err)
}
log.Debug("Creating `" + OutputDir + "` directory")
if err = os.MkdirAll(OutputDir, os.ModePerm); err != nil {
log.Fatal(err)
}
log.Debug("Building Authelia frontend...")
buildFrontend(branch)
log.Debug("Building swagger-ui frontend...")
buildSwagger()
buildkite, _ := cobraCmd.Flags().GetBool("buildkite")
if buildkite {
log.Info("Building Authelia Go binaries with gox...")
buildAutheliaBinaryGOX(xflags)
} else {
log.Info("Building Authelia Go binary...")
buildAutheliaBinaryGO(xflags)
}
cleanAssets()
}
func buildAutheliaBinaryGOX(xflags []string) {
var wg sync.WaitGroup
s := time.Now()
@ -52,7 +109,9 @@ func buildAutheliaBinary(xflags []string, buildkite bool) {
e := time.Since(s)
log.Debugf("Binary compilation completed in %s.", e)
} else {
}
func buildAutheliaBinaryGO(xflags []string) {
cmd := utils.CommandWithStdout("go", "build", "-buildmode=pie", "-trimpath", "-o", OutputDir+"/authelia", "-ldflags", "-linkmode=external -s -w "+strings.Join(xflags, " "), "./cmd/authelia/")
cmd.Env = append(os.Environ(),
@ -62,7 +121,6 @@ func buildAutheliaBinary(xflags []string, buildkite bool) {
if err != nil {
log.Fatal(err)
}
}
}
func buildFrontend(branch string) {
@ -133,47 +191,3 @@ func cleanAssets() {
log.Fatal(err)
}
}
// Build build Authelia.
func Build(cobraCmd *cobra.Command, args []string) {
buildkite, _ := cobraCmd.Flags().GetBool("buildkite")
branch := os.Getenv("BUILDKITE_BRANCH")
if strings.HasPrefix(branch, "renovate/") {
buildFrontend(branch)
log.Info("Skip building Authelia for deps...")
os.Exit(0)
}
log.Info("Building Authelia...")
Clean(cobraCmd, args)
xflags, err := getXFlags(branch, os.Getenv("BUILDKITE_BUILD_NUMBER"), "")
if err != nil {
log.Fatal(err)
}
log.Debug("Creating `" + OutputDir + "` directory")
err = os.MkdirAll(OutputDir, os.ModePerm)
if err != nil {
log.Fatal(err)
}
log.Debug("Building Authelia frontend...")
buildFrontend(branch)
log.Debug("Building swagger-ui frontend...")
buildSwagger()
if buildkite {
log.Debug("Building Authelia Go binaries with gox...")
} else {
log.Debug("Building Authelia Go binary...")
}
buildAutheliaBinary(xflags, buildkite)
cleanAssets()
}

View File

@ -1,4 +1,4 @@
package main
package cmd
import (
log "github.com/sirupsen/logrus"
@ -7,12 +7,23 @@ import (
"github.com/authelia/authelia/v4/internal/utils"
)
// RunCI run the CI scripts.
func RunCI(cmd *cobra.Command, args []string) {
func newCICmd() (cmd *cobra.Command) {
cmd = &cobra.Command{
Use: "ci",
Short: cmdCIShort,
Long: cmdCILong,
Example: cmdCIExample,
Args: cobra.NoArgs,
Run: cmdCIRun,
}
return cmd
}
func cmdCIRun(cmd *cobra.Command, _ []string) {
log.Info("=====> Build stage <=====")
buildkite, _ := cmd.Flags().GetBool("buildkite")
if buildkite {
if buildkite, _ := cmd.Flags().GetBool("buildkite"); buildkite {
if err := utils.CommandWithStdout("authelia-scripts", "--log-level", "debug", "--buildkite", "build").Run(); err != nil {
log.Fatal(err)
}

View File

@ -0,0 +1,30 @@
package cmd
import (
"os"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
func newCleanCmd() (cmd *cobra.Command) {
cmd = &cobra.Command{
Use: "clean",
Short: cmdCleanShort,
Long: cmdCleanLong,
Example: cmdCleanExample,
Args: cobra.NoArgs,
Run: cmdCleanRun,
}
return cmd
}
func cmdCleanRun(_ *cobra.Command, _ []string) {
log.Debug("Removing `" + OutputDir + "` directory")
err := os.RemoveAll(OutputDir)
if err != nil {
panic(err)
}
}

View File

@ -0,0 +1,128 @@
package cmd
// OutputDir the output directory where the built version of Authelia is located.
var OutputDir = "dist"
// DockerImageName the official name of Authelia docker image.
var DockerImageName = "authelia/authelia"
// IntermediateDockerImageName local name of the docker image.
var IntermediateDockerImageName = "authelia:dist"
const dockerhub = "docker.io"
const ghcr = "ghcr.io"
const masterTag = "master"
const stringFalse = "false"
const webDirectory = "web"
const fmtLDFLAGSX = "-X 'github.com/authelia/authelia/v4/internal/utils.%s=%s'"
const (
cmdRootShort = "A utility used in the Authelia development process."
cmdRootLong = `The authelia-scripts utility is utilized by developers and the CI/CD pipeline for configuring
testing suites and various other aspects of the environment.
It can be used to automate or manually run unit testing, integration testing, etc.`
cmdRootExample = `authelia-scripts help`
cmdBootstrapShort = "Prepare environment for development and testing"
cmdBootstrapLong = `Prepare environment for development and testing.`
cmdBootstrapExample = `authelia-scripts bootstrap`
cmdBuildShort = "Build Authelia binary and static assets"
cmdBuildLong = `Build Authelia binary and static assets.`
cmdBuildExample = `authelia-scripts build`
cmdCleanShort = "Clean build artifacts"
cmdCleanLong = `Clean build artifacts.`
cmdCleanExample = `authelia-scripts clean`
cmdCIShort = "Run the continuous integration script"
cmdCILong = `Run the continuous integration script.`
cmdCIExample = `authelia-scripts ci`
cmdDockerShort = "Commands related to building and publishing docker image"
cmdDockerLong = `Commands related to building and publishing docker image.`
cmdDockerExample = `authelia-scripts docker`
cmdDockerBuildShort = "Build the docker image of Authelia"
cmdDockerBuildLong = `Build the docker image of Authelia.`
cmdDockerBuildExample = `authelia-scripts docker build`
cmdDockerPushManifestShort = "Push Authelia docker manifest to the Docker registries"
cmdDockerPushManifestLong = `Push Authelia docker manifest to the Docker registries.`
cmdDockerPushManifestExample = `authelia-scripts docker push-manifest`
cmdServeShort = "Serve compiled version of Authelia"
cmdServeLong = `Serve compiled version of Authelia.`
cmdServeExample = `authelia-scripts serve test.yml`
cmdSuitesShort = "Commands related to suites management"
cmdSuitesLong = `Commands related to suites management.`
cmdSuitesExample = `authelia-scripts suites`
cmdSuitesListShort = "List available suites"
cmdSuitesListLong = `List available suites.
Suites can be ran with the authelia-scripts suites test [suite] command.`
cmdSuitesListExample = `authelia-scripts suites list`
cmdSuitesTestShort = "Run a test suite"
cmdSuitesTestLong = `Run a test suite.
Suites can be listed with the authelia-scripts suites list command.`
cmdSuitesTestExample = `authelia-scripts suites test Standalone`
cmdSuitesSetupShort = "Setup a test suite environment"
cmdSuitesSetupLong = `Setup a test suite environment.
Suites can be listed with the authelia-scripts suites list command.`
cmdSuitesSetupExample = `authelia-scripts suites setup Standalone`
cmdSuitesTeardownShort = "Teardown a test suite environment"
cmdSuitesTeardownLong = `Teardown a test suite environment.
Suites can be listed with the authelia-scripts suites list command.`
cmdSuitesTeardownExample = `authelia-scripts suites setup Standalone`
cmdUnitTestShort = "Run unit tests"
cmdUnitTestLong = `Run unit tests.`
cmdUnitTestExample = `authelia-scripts unittest`
cmdXFlagsShort = "Generate X LDFlags for building Authelia"
cmdXFlagsLong = `Generate X LDFlags for building Authelia.`
cmdXFlagsExample = `authelia-scripts xflags`
)

View File

@ -1,4 +1,4 @@
package main
package cmd
import (
"errors"
@ -23,8 +23,103 @@ var ignoredSuffixes = regexp.MustCompile("alpha|beta")
var publicRepo = regexp.MustCompile(`.*:.*`)
var tags = dockerTags.FindStringSubmatch(ciTag)
func init() {
DockerBuildCmd.PersistentFlags().StringVar(&container, "container", defaultContainer, "target container among: "+strings.Join(containers, ", "))
func newDockerCmd() (cmd *cobra.Command) {
cmd = &cobra.Command{
Use: "docker",
Short: cmdDockerShort,
Long: cmdDockerLong,
Example: cmdDockerExample,
Args: cobra.NoArgs,
Run: cmdDockerBuildRun,
}
cmd.AddCommand(newDockerBuildCmd(), newDockerPushManifestCmd())
return cmd
}
func newDockerBuildCmd() (cmd *cobra.Command) {
cmd = &cobra.Command{
Use: "build",
Short: cmdDockerBuildShort,
Long: cmdDockerBuildLong,
Example: cmdDockerBuildExample,
Args: cobra.NoArgs,
Run: cmdDockerBuildRun,
}
cmd.PersistentFlags().StringVar(&container, "container", defaultContainer, "target container among: "+strings.Join(containers, ", "))
return cmd
}
func newDockerPushManifestCmd() (cmd *cobra.Command) {
cmd = &cobra.Command{
Use: "push-manifest",
Short: cmdDockerPushManifestShort,
Long: cmdDockerPushManifestLong,
Example: cmdDockerPushManifestExample,
Args: cobra.NoArgs,
Run: cmdDockerPushManifestRun,
}
return cmd
}
func cmdDockerBuildRun(_ *cobra.Command, _ []string) {
log.Infof("Building Docker image %s...", DockerImageName)
checkContainerIsSupported(container)
err := dockerBuildOfficialImage(container)
if err != nil {
log.Fatal(err)
}
docker := &Docker{}
err = docker.Tag(IntermediateDockerImageName, DockerImageName)
if err != nil {
log.Fatal(err)
}
}
func cmdDockerPushManifestRun(_ *cobra.Command, _ []string) {
docker := &Docker{}
switch {
case ciTag != "":
if len(tags) == 4 {
log.Infof("Detected tags: '%s' | '%s' | '%s'", tags[1], tags[2], tags[3])
login(docker, dockerhub)
login(docker, ghcr)
deployManifest(docker, tags[1])
publishDockerReadme(docker)
if !ignoredSuffixes.MatchString(ciTag) {
deployManifest(docker, tags[2])
deployManifest(docker, tags[3])
deployManifest(docker, "latest")
publishDockerReadme(docker)
}
} else {
log.Fatal("Docker manifest will not be published, the specified tag does not conform to the standard")
}
case ciBranch != masterTag && !publicRepo.MatchString(ciBranch):
login(docker, dockerhub)
login(docker, ghcr)
deployManifest(docker, ciBranch)
case ciBranch != masterTag && publicRepo.MatchString(ciBranch):
login(docker, dockerhub)
login(docker, ghcr)
deployManifest(docker, "PR"+ciPullRequest)
case ciBranch == masterTag && ciPullRequest == stringFalse:
login(docker, dockerhub)
login(docker, ghcr)
deployManifest(docker, "master")
publishDockerReadme(docker)
default:
log.Info("Docker manifest will not be published")
}
}
func checkContainerIsSupported(container string) {
@ -51,37 +146,6 @@ func dockerBuildOfficialImage(arch string) error {
strings.Join(flags, " "))
}
// DockerBuildCmd Command for building docker image of Authelia.
var DockerBuildCmd = &cobra.Command{
Use: "build",
Short: "Build the docker image of Authelia",
Run: func(cmd *cobra.Command, args []string) {
log.Infof("Building Docker image %s...", DockerImageName)
checkContainerIsSupported(container)
err := dockerBuildOfficialImage(container)
if err != nil {
log.Fatal(err)
}
docker := &Docker{}
err = docker.Tag(IntermediateDockerImageName, DockerImageName)
if err != nil {
log.Fatal(err)
}
},
}
// DockerManifestCmd Command for pushing Authelia docker manifest to DockerHub.
var DockerManifestCmd = &cobra.Command{
Use: "push-manifest",
Short: "Publish Authelia docker manifest to Docker Hub",
Run: func(cmd *cobra.Command, args []string) {
publishDockerManifest()
},
}
func login(docker *Docker, registry string) {
username := ""
password := ""
@ -122,45 +186,6 @@ func deployManifest(docker *Docker, tag string) {
}
}
func publishDockerManifest() {
docker := &Docker{}
switch {
case ciTag != "":
if len(tags) == 4 {
log.Infof("Detected tags: '%s' | '%s' | '%s'", tags[1], tags[2], tags[3])
login(docker, dockerhub)
login(docker, ghcr)
deployManifest(docker, tags[1])
publishDockerReadme(docker)
if !ignoredSuffixes.MatchString(ciTag) {
deployManifest(docker, tags[2])
deployManifest(docker, tags[3])
deployManifest(docker, "latest")
publishDockerReadme(docker)
}
} else {
log.Fatal("Docker manifest will not be published, the specified tag does not conform to the standard")
}
case ciBranch != masterTag && !publicRepo.MatchString(ciBranch):
login(docker, dockerhub)
login(docker, ghcr)
deployManifest(docker, ciBranch)
case ciBranch != masterTag && publicRepo.MatchString(ciBranch):
login(docker, dockerhub)
login(docker, ghcr)
deployManifest(docker, "PR"+ciPullRequest)
case ciBranch == masterTag && ciPullRequest == stringFalse:
login(docker, dockerhub)
login(docker, ghcr)
deployManifest(docker, "master")
publishDockerReadme(docker)
default:
log.Info("Docker manifest will not be published")
}
}
func publishDockerReadme(docker *Docker) {
log.Info("Docker pushing README.md to Docker Hub")

View File

@ -0,0 +1,11 @@
package cmd
import (
"errors"
)
// ErrNotAvailableSuite error raised when suite is not available.
var ErrNotAvailableSuite = errors.New("unavailable suite")
// ErrNoRunningSuite error raised when no suite is running.
var ErrNoRunningSuite = errors.New("no running suite")

View File

@ -1,4 +1,4 @@
package main
package cmd
import (
"fmt"

View File

@ -1,4 +1,4 @@
package main
package cmd
import (
"github.com/authelia/authelia/v4/internal/utils"

View File

@ -0,0 +1,17 @@
package cmd
import (
log "github.com/sirupsen/logrus"
)
var logLevel string
func levelStringToLevel(level string) log.Level {
if level == "debug" {
return log.DebugLevel
} else if level == "warning" {
return log.WarnLevel
}
return log.InfoLevel
}

View File

@ -0,0 +1,29 @@
package cmd
import (
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
// NewRootCmd returns the root authelia-scripts cmd.
func NewRootCmd() (cmd *cobra.Command) {
cmd = &cobra.Command{
Use: "authelia-scripts",
Short: cmdRootShort,
Long: cmdRootLong,
Example: cmdRootExample,
}
cmd.PersistentFlags().Bool("buildkite", false, "Set CI flag for Buildkite")
cmd.PersistentFlags().StringVar(&logLevel, "log-level", "info", "Set the log level for the command")
cmd.AddCommand(newBootstrapCmd(), newBuildCmd(), newCleanCmd(), newCICmd(), newDockerCmd(), newServeCmd(), newSuitesCmd(), newUnitTestCmd(), newXFlagsCmd())
cobra.OnInitialize(cmdRootInit)
return cmd
}
func cmdRootInit() {
log.SetLevel(levelStringToLevel(logLevel))
}

View File

@ -0,0 +1,27 @@
package cmd
import (
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/authelia/authelia/v4/internal/utils"
)
func newServeCmd() (cmd *cobra.Command) {
cmd = &cobra.Command{
Use: "serve [config]",
Short: cmdServeShort,
Long: cmdServeLong,
Example: cmdServeExample,
Args: cobra.MinimumNArgs(1),
Run: cmdServeRun,
}
return cmd
}
func cmdServeRun(_ *cobra.Command, args []string) {
log.Infof("Running Authelia with config %s...", args[0])
execCmd := utils.CommandWithStdout(OutputDir+"/authelia", "--config", args[0])
utils.RunCommandUntilCtrlC(execCmd)
}

View File

@ -1,4 +1,4 @@
package main
package cmd
import (
"errors"
@ -17,40 +17,88 @@ import (
"github.com/authelia/authelia/v4/internal/utils"
)
// ErrNotAvailableSuite error raised when suite is not available.
var ErrNotAvailableSuite = errors.New("unavailable suite")
var (
runningSuiteFile = ".suite"
failfast, headless bool
testPattern string
)
// ErrNoRunningSuite error raised when no suite is running.
var ErrNoRunningSuite = errors.New("no running suite")
func newSuitesCmd() (cmd *cobra.Command) {
cmd = &cobra.Command{
Use: "suites",
Short: cmdSuitesShort,
Long: cmdSuitesLong,
Example: cmdSuitesExample,
Run: cmdSuitesListRun,
Args: cobra.NoArgs,
}
// runningSuiteFile name of the file containing the currently running suite.
var runningSuiteFile = ".suite"
cmd.AddCommand(newSuitesListCmd(), newSuitesSetupCmd(), newSuitesTestCmd(), newSuitesTeardownCmd())
var failfast bool
var headless bool
var testPattern string
func init() {
SuitesTestCmd.Flags().BoolVar(&failfast, "failfast", false, "Stops tests on first failure")
SuitesTestCmd.Flags().BoolVar(&headless, "headless", false, "Run tests in headless mode")
SuitesTestCmd.Flags().StringVar(&testPattern, "test", "", "The single test to run")
return cmd
}
// SuitesListCmd Command for listing the available suites.
var SuitesListCmd = &cobra.Command{
func newSuitesListCmd() (cmd *cobra.Command) {
cmd = &cobra.Command{
Use: "list",
Short: "List available suites.",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println(strings.Join(listSuites(), "\n"))
},
Args: cobra.ExactArgs(0),
Short: cmdSuitesListShort,
Long: cmdSuitesListLong,
Example: cmdSuitesListExample,
Run: cmdSuitesListRun,
Args: cobra.NoArgs,
}
return cmd
}
// SuitesSetupCmd Command to setup a suite environment.
var SuitesSetupCmd = &cobra.Command{
func newSuitesSetupCmd() (cmd *cobra.Command) {
cmd = &cobra.Command{
Use: "setup [suite]",
Short: "Setup a Go suite environment. Suites can be listed using the list command.",
Run: func(cmd *cobra.Command, args []string) {
Short: cmdSuitesSetupShort,
Long: cmdSuitesSetupLong,
Example: cmdSuitesSetupExample,
Run: cmdSuitesSetupRun,
Args: cobra.MaximumNArgs(1),
}
return cmd
}
func newSuitesTeardownCmd() (cmd *cobra.Command) {
cmd = &cobra.Command{
Use: "teardown [suite]",
Short: cmdSuitesTeardownShort,
Long: cmdSuitesTeardownLong,
Example: cmdSuitesTeardownExample,
Run: cmdSuitesTeardownRun,
Args: cobra.MaximumNArgs(1),
}
return cmd
}
func newSuitesTestCmd() (cmd *cobra.Command) {
cmd = &cobra.Command{
Use: "test [suite]",
Short: cmdSuitesTestShort,
Long: cmdSuitesTestLong,
Example: cmdSuitesTestExample,
Run: cmdSuitesTestRun,
Args: cobra.MaximumNArgs(1),
}
cmd.Flags().BoolVar(&failfast, "failfast", false, "Stops tests on first failure")
cmd.Flags().BoolVar(&headless, "headless", false, "Run tests in headless mode")
cmd.Flags().StringVar(&testPattern, "test", "", "The single test to run")
return cmd
}
func cmdSuitesListRun(_ *cobra.Command, _ []string) {
fmt.Println(strings.Join(listSuites(), "\n"))
}
func cmdSuitesSetupRun(_ *cobra.Command, args []string) {
providedSuite := args[0]
runningSuite, err := getRunningSuite()
@ -65,15 +113,9 @@ var SuitesSetupCmd = &cobra.Command{
if err := setupSuite(providedSuite); err != nil {
log.Fatal(err)
}
},
Args: cobra.ExactArgs(1),
}
// SuitesTeardownCmd Command for tearing down a suite environment.
var SuitesTeardownCmd = &cobra.Command{
Use: "teardown [suite]",
Short: "Teardown a Go suite environment. Suites can be listed using the list command.",
Run: func(cmd *cobra.Command, args []string) {
func cmdSuitesTeardownRun(_ *cobra.Command, args []string) {
var suiteName string
if len(args) == 1 {
suiteName = args[0]
@ -93,16 +135,38 @@ var SuitesTeardownCmd = &cobra.Command{
if err := teardownSuite(suiteName); err != nil {
log.Fatal(err)
}
},
Args: cobra.MaximumNArgs(1),
}
// SuitesTestCmd Command for testing a suite.
var SuitesTestCmd = &cobra.Command{
Use: "test [suite]",
Short: "Test a suite. Suites can be listed using the list command.",
Run: testSuite,
Args: cobra.MaximumNArgs(1),
func cmdSuitesTestRun(_ *cobra.Command, args []string) {
runningSuite, err := getRunningSuite()
if err != nil {
log.Fatal(err)
}
// If suite(s) are provided as argument.
if len(args) >= 1 {
suiteArg := args[0]
if runningSuite != "" && suiteArg != runningSuite {
log.Fatal(errors.New("Running suite (" + runningSuite + ") is different than suite(s) to be tested (" + suiteArg + "). Shutdown running suite and retry"))
}
if err := runMultipleSuitesTests(strings.Split(suiteArg, ","), runningSuite == ""); err != nil {
log.Fatal(err)
}
} else {
if runningSuite != "" {
fmt.Println("Running suite (" + runningSuite + ") detected. Run tests of that suite")
if err := runSuiteTests(runningSuite, false); err != nil {
log.Fatal(err)
}
} else {
fmt.Println("No suite provided therefore all suites will be tested")
if err := runAllSuites(); err != nil {
log.Fatal(err)
}
}
}
}
func listSuites() []string {
@ -114,9 +178,9 @@ func listSuites() []string {
}
func checkSuiteAvailable(suite string) error {
suites := listSuites()
suiteNames := listSuites()
for _, s := range suites {
for _, s := range suiteNames {
if s == suite {
return nil
}
@ -218,38 +282,6 @@ func teardownSuite(suiteName string) error {
return runSuiteSetupTeardown("teardown", suiteName)
}
func testSuite(cmd *cobra.Command, args []string) {
runningSuite, err := getRunningSuite()
if err != nil {
log.Fatal(err)
}
// If suite(s) are provided as argument.
if len(args) >= 1 {
suiteArg := args[0]
if runningSuite != "" && suiteArg != runningSuite {
log.Fatal(errors.New("Running suite (" + runningSuite + ") is different than suite(s) to be tested (" + suiteArg + "). Shutdown running suite and retry"))
}
if err := runMultipleSuitesTests(strings.Split(suiteArg, ","), runningSuite == ""); err != nil {
log.Fatal(err)
}
} else {
if runningSuite != "" {
fmt.Println("Running suite (" + runningSuite + ") detected. Run tests of that suite")
if err := runSuiteTests(runningSuite, false); err != nil {
log.Fatal(err)
}
} else {
fmt.Println("No suite provided therefore all suites will be tested")
if err := runAllSuites(); err != nil {
log.Fatal(err)
}
}
}
}
func getRunningSuite() (string, error) {
exist, err := utils.FileExists(runningSuiteFile)

View File

@ -0,0 +1,7 @@
package cmd
// HostEntry represents an entry in /etc/hosts.
type HostEntry struct {
Domain string
IP string
}

View File

@ -1,4 +1,4 @@
package main
package cmd
import (
"os"
@ -9,8 +9,20 @@ import (
"github.com/authelia/authelia/v4/internal/utils"
)
// RunUnitTest run the unit tests.
func RunUnitTest(cobraCmd *cobra.Command, args []string) {
func newUnitTestCmd() (cmd *cobra.Command) {
cmd = &cobra.Command{
Use: "unittest",
Short: cmdUnitTestShort,
Long: cmdUnitTestLong,
Example: cmdUnitTestExample,
Args: cobra.NoArgs,
Run: cmdUnitTestRun,
}
return cmd
}
func cmdUnitTestRun(_ *cobra.Command, _ []string) {
log.SetLevel(log.TraceLevel)
if err := utils.Shell("go test -coverprofile=coverage.txt -covermode=atomic $(go list ./... | grep -v suites)").Run(); err != nil {

View File

@ -0,0 +1,44 @@
package cmd
import (
"fmt"
"strings"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
func newXFlagsCmd() (cmd *cobra.Command) {
cmd = &cobra.Command{
Use: "xflags",
Short: cmdXFlagsShort,
Long: cmdXFlagsLong,
Example: cmdXFlagsExample,
Args: cobra.NoArgs,
Run: cmdXFlagsRun,
}
cmd.Flags().StringP("build", "b", "0", "Sets the BuildNumber flag value")
cmd.Flags().StringP("extra", "e", "", "Sets the BuildExtra flag value")
return cmd
}
func cmdXFlagsRun(cobraCmd *cobra.Command, _ []string) {
build, err := cobraCmd.Flags().GetString("build")
if err != nil {
log.Fatal(err)
}
extra, err := cobraCmd.Flags().GetString("extra")
if err != nil {
log.Fatal(err)
}
flags, err := getXFlags("", build, extra)
if err != nil {
log.Fatal(err)
}
fmt.Println(strings.Join(flags, " "))
}

View File

@ -1,18 +0,0 @@
package main
import (
"os"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
// Clean artifacts built and installed by authelia-scripts.
func Clean(cobraCmd *cobra.Command, args []string) {
log.Debug("Removing `" + OutputDir + "` directory")
err := os.RemoveAll(OutputDir)
if err != nil {
panic(err)
}
}

View File

@ -1,15 +0,0 @@
package main
import (
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/authelia/authelia/v4/internal/utils"
)
// ServeCmd serve Authelia with the provided configuration.
func ServeCmd(cmd *cobra.Command, args []string) {
log.Infof("Running Authelia with config %s...", args[0])
execCmd := utils.CommandWithStdout(OutputDir+"/authelia", "--config", args[0])
utils.RunCommandUntilCtrlC(execCmd)
}

View File

@ -1,39 +0,0 @@
package main
import (
"fmt"
"strings"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
func init() {
xflagsCmd.Flags().StringP("build", "b", "0", "Sets the BuildNumber flag value")
xflagsCmd.Flags().StringP("extra", "e", "", "Sets the BuildExtra flag value")
}
var xflagsCmd = &cobra.Command{
Use: "xflags",
Run: runXFlags,
Short: "Generate X LDFlags for building Authelia",
}
func runXFlags(cobraCmd *cobra.Command, _ []string) {
build, err := cobraCmd.Flags().GetString("build")
if err != nil {
log.Fatal(err)
}
extra, err := cobraCmd.Flags().GetString("extra")
if err != nil {
log.Fatal(err)
}
flags, err := getXFlags("", build, extra)
if err != nil {
log.Fatal(err)
}
fmt.Println(strings.Join(flags, " "))
}

View File

@ -1,19 +0,0 @@
package main
// OutputDir the output directory where the built version of Authelia is located.
var OutputDir = "dist"
// DockerImageName the official name of Authelia docker image.
var DockerImageName = "authelia/authelia"
// IntermediateDockerImageName local name of the docker image.
var IntermediateDockerImageName = "authelia:dist"
const dockerhub = "docker.io"
const ghcr = "ghcr.io"
const masterTag = "master"
const stringFalse = "false"
const webDirectory = "web"
const fmtLDFLAGSX = "-X 'github.com/authelia/authelia/v4/internal/utils.%s=%s'"

View File

@ -5,151 +5,12 @@ package main
import (
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/authelia/authelia/v4/internal/commands"
"github.com/authelia/authelia/v4/internal/utils"
"github.com/authelia/authelia/v4/cmd/authelia-scripts/cmd"
)
var buildkite bool
var logLevel string
// AutheliaCommandDefinition is the definition of one authelia-scripts command.
type AutheliaCommandDefinition struct {
Name string
Short string
Long string
CommandLine string
Args cobra.PositionalArgs
Func func(cmd *cobra.Command, args []string)
SubCommands []*cobra.Command
}
// CobraCommands list of cobra commands.
type CobraCommands = []*cobra.Command
// Commands is the list of commands of authelia-scripts.
var Commands = []AutheliaCommandDefinition{
{
Name: "bootstrap",
Short: "Prepare environment for development and testing.",
Long: `Prepare environment for development and testing. This command prepares docker
images and download tools like Kind for Kubernetes testing.`,
Func: Bootstrap,
},
{
Name: "build",
Short: "Build Authelia binary and static assets",
Func: Build,
},
{
Name: "clean",
Short: "Clean build artifacts",
Func: Clean,
},
{
Name: "docker",
Short: "Commands related to building and publishing docker image",
SubCommands: CobraCommands{DockerBuildCmd, DockerManifestCmd},
},
{
Name: "serve [config]",
Short: "Serve compiled version of Authelia",
Func: ServeCmd,
Args: cobra.MinimumNArgs(1),
},
{
Name: "suites",
Short: "Commands related to suites management",
SubCommands: CobraCommands{
SuitesTestCmd,
SuitesListCmd,
SuitesSetupCmd,
SuitesTeardownCmd,
},
},
{
Name: "ci",
Short: "Run continuous integration script",
Func: RunCI,
},
{
Name: "unittest",
Short: "Run unit tests",
Func: RunUnitTest,
},
}
func levelStringToLevel(level string) log.Level {
if level == "debug" {
return log.DebugLevel
} else if level == "warning" {
return log.WarnLevel
}
return log.InfoLevel
}
func main() {
var rootCmd = &cobra.Command{Use: "authelia-scripts"}
cobraCommands := make([]*cobra.Command, 0)
for _, autheliaCommand := range Commands {
var fn func(cobraCmd *cobra.Command, args []string)
if autheliaCommand.CommandLine != "" {
cmdline := autheliaCommand.CommandLine
fn = func(cobraCmd *cobra.Command, args []string) {
cmd := utils.CommandWithStdout(cmdline, args...)
err := cmd.Run()
if err != nil {
panic(err)
}
}
} else if autheliaCommand.Func != nil {
fn = autheliaCommand.Func
}
command := &cobra.Command{
Use: autheliaCommand.Name,
Short: autheliaCommand.Short,
}
if autheliaCommand.Long != "" {
command.Long = autheliaCommand.Long
}
if fn != nil {
command.Run = fn
}
if autheliaCommand.Args != nil {
command.Args = autheliaCommand.Args
}
if autheliaCommand.SubCommands != nil {
command.AddCommand(autheliaCommand.SubCommands...)
}
cobraCommands = append(cobraCommands, command)
}
cobraCommands = append(cobraCommands, commands.NewHashPasswordCmd(), commands.NewCertificatesCmd(), commands.NewRSACmd(), NewRunGenCmd(), xflagsCmd)
rootCmd.PersistentFlags().BoolVar(&buildkite, "buildkite", false, "Set CI flag for Buildkite")
rootCmd.PersistentFlags().StringVar(&logLevel, "log-level", "info", "Set the log level for the command")
rootCmd.AddCommand(cobraCommands...)
cobra.OnInitialize(initConfig)
err := rootCmd.Execute()
if err != nil {
if err := cmd.NewRootCmd().Execute(); err != nil {
log.Fatal(err)
}
}
func initConfig() {
log.SetLevel(levelStringToLevel(logLevel))
}

2
go.mod
View File

@ -46,6 +46,7 @@ require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dgraph-io/ristretto v0.1.0 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
@ -87,6 +88,7 @@ require (
github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/common v0.32.1 // indirect
github.com/prometheus/procfs v0.7.3 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/savsgio/dictpool v0.0.0-20220406081701-03de5edb2e6d // indirect
github.com/savsgio/gotils v0.0.0-20220530130905-52f3993e8d6d // indirect
github.com/spf13/afero v1.6.0 // indirect

4
go.sum
View File

@ -158,9 +158,11 @@ github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.1 h1:r/myEWzV9lfsM1tFLgDyu0atFtJ1fXn261LKYj/3DxU=
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
@ -1185,8 +1187,10 @@ github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
github.com/rubenv/sql-migrate v0.0.0-20190212093014-1007f53448d7/go.mod h1:WS0rl9eEliYI8DPnr3TOwz4439pay+qNgzJoVya/DmY=
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=

View File

@ -18,7 +18,9 @@ import (
func newAccessControlCommand() (cmd *cobra.Command) {
cmd = &cobra.Command{
Use: "access-control",
Short: "Helpers for the access control system",
Short: cmdAutheliaAccessControlShort,
Long: cmdAutheliaAccessControlLong,
Example: cmdAutheliaAccessControlExample,
}
cmd.AddCommand(
@ -31,12 +33,13 @@ func newAccessControlCommand() (cmd *cobra.Command) {
func newAccessControlCheckCommand() (cmd *cobra.Command) {
cmd = &cobra.Command{
Use: "check-policy",
Short: "Checks a request against the access control rules to determine what policy would be applied",
Long: accessControlPolicyCheckLong,
Short: cmdAutheliaAccessControlCheckPolicyShort,
Long: cmdAutheliaAccessControlCheckPolicyLong,
Example: cmdAutheliaAccessControlCheckPolicyExample,
RunE: accessControlCheckRunE,
}
cmdWithConfigFlags(cmd, false, []string{"config.yml"})
cmdWithConfigFlags(cmd, false, []string{"configuration.yml"})
cmd.Flags().String("url", "", "the url of the object")
cmd.Flags().String("method", "GET", "the HTTP method of the object")

View File

@ -12,8 +12,9 @@ import (
func newBuildInfoCmd() (cmd *cobra.Command) {
cmd = &cobra.Command{
Use: "build-info",
Short: "Show the build information of Authelia",
Long: buildLong,
Short: cmdAutheliaBuildInfoShort,
Long: cmdAutheliaBuildInfoLong,
Example: cmdAutheliaBuildInfoExample,
RunE: cmdBuildInfoRunE,
Args: cobra.NoArgs,
}

View File

@ -13,11 +13,12 @@ import (
"github.com/authelia/authelia/v4/internal/utils"
)
// NewCertificatesCmd returns a new Certificates Cmd.
func NewCertificatesCmd() (cmd *cobra.Command) {
func newCertificatesCmd() (cmd *cobra.Command) {
cmd = &cobra.Command{
Use: "certificates",
Short: "Commands related to certificate generation",
Short: cmdAutheliaCertificatesShort,
Long: cmdAutheliaCertificatesLong,
Example: cmdAutheliaCertificatesExample,
Args: cobra.NoArgs,
}
@ -36,7 +37,9 @@ func NewCertificatesCmd() (cmd *cobra.Command) {
func newCertificatesGenerateCmd() (cmd *cobra.Command) {
cmd = &cobra.Command{
Use: "generate",
Short: "Generate a self-signed certificate",
Short: cmdAutheliaCertificatesGenerateShort,
Long: cmdAutheliaCertificatesGenerateLong,
Example: cmdAutheliaCertificatesGenerateExample,
Args: cobra.NoArgs,
Run: cmdCertificatesGenerateRun,
}

View File

@ -1,45 +0,0 @@
package commands
import (
"fmt"
"os"
"github.com/spf13/cobra"
)
func newCompletionCmd() (cmd *cobra.Command) {
cmd = &cobra.Command{
Use: "completion [bash|zsh|fish|powershell]",
Short: "Generate completion script",
Long: completionLong,
Args: cobra.ExactValidArgs(1),
ValidArgs: []string{"bash", "zsh", "fish", "powershell"},
DisableFlagsInUseLine: true,
Run: cmdCompletionRun,
}
return cmd
}
func cmdCompletionRun(cmd *cobra.Command, args []string) {
var err error
switch args[0] {
case "bash":
err = cmd.Root().GenBashCompletion(os.Stdout)
case "zsh":
err = cmd.Root().GenZshCompletion(os.Stdout)
case "fish":
err = cmd.Root().GenFishCompletion(os.Stdout, true)
case "powershell":
err = cmd.Root().GenPowerShellCompletionWithDesc(os.Stdout)
default:
fmt.Printf("Invalid shell provided for completion command: %s\n", args[0])
os.Exit(1)
}
if err != nil {
fmt.Printf("Error generating completion: %v\n", err)
os.Exit(1)
}
}

View File

@ -4,21 +4,22 @@ import (
"errors"
)
const cmdAutheliaExample = `authelia --config /etc/authelia/config.yml --config /etc/authelia/access-control.yml
authelia --config /etc/authelia/config.yml,/etc/authelia/access-control.yml
authelia --config /etc/authelia/config/
`
const (
fmtCmdAutheliaShort = "authelia %s"
const fmtAutheliaLong = `authelia %s
fmtCmdAutheliaLong = `authelia %s
An open-source authentication and authorization server providing
two-factor authentication and single sign-on (SSO) for your
applications via a web portal.
Documentation is available at: https://www.authelia.com/docs
`
Documentation is available at: https://www.authelia.com/docs`
const fmtAutheliaBuild = `Last Tag: %s
cmdAutheliaExample = `authelia --config /etc/authelia/config.yml --config /etc/authelia/access-control.yml
authelia --config /etc/authelia/config.yml,/etc/authelia/access-control.yml
authelia --config /etc/authelia/config/`
fmtAutheliaBuild = `Last Tag: %s
State: %s
Branch: %s
Commit: %s
@ -29,7 +30,9 @@ Build Date: %s
Extra: %s
`
const buildLong = `Show the build information of Authelia
cmdAutheliaBuildInfoShort = "Show the build information of Authelia"
cmdAutheliaBuildInfoLong = `Show the build information of Authelia.
This outputs detailed version information about the specific version
of the Authelia binary. This information is embedded into Authelia
@ -39,48 +42,17 @@ This could be vital in debugging if you're not using a particular
tagged build of Authelia. It's suggested to provide it along with
your issue.
`
cmdAutheliaBuildInfoExample = `authelia build-info`
const completionLong = `To load completions:
cmdAutheliaAccessControlShort = "Helpers for the access control system"
Bash:
cmdAutheliaAccessControlLong = `Helpers for the access control system.`
$ source <(authelia completion bash)
cmdAutheliaAccessControlExample = `authelia access-control --help`
# To load completions for each session, execute once:
# Linux:
$ authelia completion bash > /etc/bash_completion.d/authelia
# macOS:
$ authelia completion bash > /usr/local/etc/bash_completion.d/authelia
cmdAutheliaAccessControlCheckPolicyShort = "Checks a request against the access control rules to determine what policy would be applied"
Zsh:
# If shell completion is not already enabled in your environment,
# you will need to enable it. You can execute the following once:
$ echo "autoload -U compinit; compinit" >> ~/.zshrc
# To load completions for each session, execute once:
$ authelia completion zsh > "${fpath[1]}/_authelia"
# You will need to start a new shell for this setup to take effect.
fish:
$ authelia completion fish | source
# To load completions for each session, execute once:
$ authelia completion fish > ~/.config/fish/completions/authelia.fish
PowerShell:
PS> authelia completion powershell | Out-String | Invoke-Expression
# To load completions for every new session, run:
PS> authelia completion powershell > authelia.ps1
# and source this file from your PowerShell profile.
`
const accessControlPolicyCheckLong = `
cmdAutheliaAccessControlCheckPolicyLong = `
Checks a request against the access control rules to determine what policy would be applied.
Legend:
@ -97,15 +69,299 @@ Notes:
A rule that potentially matches a request will cause a redirection to occur in order to perform one-factor
authentication. This is so Authelia can adequately determine if the rule actually matches.
`
cmdAutheliaAccessControlCheckPolicyExample = `authelia access-control check-policy --config config.yml --url https://example.com
authelia access-control check-policy --config config.yml --url https://example.com --username john
authelia access-control check-policy --config config.yml --url https://example.com --groups admin,public
authelia access-control check-policy --config config.yml --url https://example.com --username john --method GET
authelia access-control check-policy --config config.yml --url https://example.com --username john --method GET --verbose`
cmdAutheliaStorageShort = "Manage the Authelia storage"
cmdAutheliaStorageLong = `Manage the Authelia storage.
This subcommand has several methods to interact with the Authelia SQL Database. This allows doing several advanced
operations which would be much harder to do manually.
`
cmdAutheliaStorageExample = `authelia storage --help`
cmdAutheliaStorageEncryptionShort = "Manage storage encryption"
cmdAutheliaStorageEncryptionLong = `Manage storage encryption.
This subcommand allows management of the storage encryption.`
cmdAutheliaStorageEncryptionExample = `authelia storage encryption --help`
cmdAutheliaStorageEncryptionCheckShort = "Checks the encryption key against the database data"
cmdAutheliaStorageEncryptionCheckLong = `Checks the encryption key against the database data.
This is useful for validating all data that can be encrypted is intact.`
cmdAutheliaStorageEncryptionCheckExample = `authelia storage encryption check
authelia storage encryption check --verbose
authelia storage encryption check --verbose --config config.yml
authelia storage encryption check --verbose --encryption-key b3453fde-ecc2-4a1f-9422-2707ddbed495 --postgres.host postgres --postgres.password autheliapw`
cmdAutheliaStorageEncryptionChangeKeyShort = "Changes the encryption key"
cmdAutheliaStorageEncryptionChangeKeyLong = `Changes the encryption key.
This subcommand allows you to change the encryption key of an Authelia SQL database.`
cmdAutheliaStorageEncryptionChangeKeyExample = `authelia storage encryption change-key --config config.yml --new-encryption-key 0e95cb49-5804-4ad9-be82-bb04a9ddecd8
authelia storage encryption change-key --encryption-key b3453fde-ecc2-4a1f-9422-2707ddbed495 --new-encryption-key 0e95cb49-5804-4ad9-be82-bb04a9ddecd8 --postgres.host postgres --postgres.password autheliapw`
cmdAutheliaStorageUserShort = "Manages user settings"
cmdAutheliaStorageUserLong = `Manages user settings.
This subcommand allows modifying and exporting user settings.`
cmdAutheliaStorageUserExample = `authelia storage user --help`
cmdAutheliaStorageUserIdentifiersShort = "Manage user opaque identifiers"
cmdAutheliaStorageUserIdentifiersLong = `Manage user opaque identifiers.
This subcommand allows performing various tasks related to the opaque identifiers for users.`
cmdAutheliaStorageUserIdentifiersExample = `authelia storage user identifiers --help`
cmdAutheliaStorageUserIdentifiersExportShort = "Export the identifiers to a YAML file"
cmdAutheliaStorageUserIdentifiersExportLong = `Export the identifiers to a YAML file.
This subcommand allows exporting the opaque identifiers for users in order to back them up.`
cmdAutheliaStorageUserIdentifiersExportExample = `authelia storage user identifiers export
authelia storage user identifiers export --file export.yaml
authelia storage user identifiers export --file export.yaml --config config.yml
authelia storage user identifiers export --file export.yaml --encryption-key b3453fde-ecc2-4a1f-9422-2707ddbed495 --postgres.host postgres --postgres.password autheliapw`
cmdAutheliaStorageUserIdentifiersImportShort = "Import the identifiers from a YAML file"
cmdAutheliaStorageUserIdentifiersImportLong = `Import the identifiers from a YAML file.
This subcommand allows you to import the opaque identifiers for users from a YAML file.
The YAML file can either be automatically generated using the authelia storage user identifiers export command, or
manually provided the file is in the same format.`
cmdAutheliaStorageUserIdentifiersImportExample = `authelia storage user identifiers import
authelia storage user identifiers import --file export.yaml
authelia storage user identifiers import --file export.yaml --config config.yml
authelia storage user identifiers import --file export.yaml --encryption-key b3453fde-ecc2-4a1f-9422-2707ddbed495 --postgres.host postgres --postgres.password autheliapw`
cmdAutheliaStorageUserIdentifiersGenerateShort = "Generate opaque identifiers in bulk"
cmdAutheliaStorageUserIdentifiersGenerateLong = `Generate opaque identifiers in bulk.
This subcommand allows various options for generating the opaque identifies for users in bulk.`
cmdAutheliaStorageUserIdentifiersGenerateExample = `authelia storage user identifiers generate --users john,mary
authelia storage user identifiers generate --users john,mary --services openid
authelia storage user identifiers generate --users john,mary --services openid --sectors=",example.com,test.com"
authelia storage user identifiers generate --users john,mary --services openid --sectors=",example.com,test.com" --config config.yml
authelia storage user identifiers generate --users john,mary --services openid --sectors=",example.com,test.com" --encryption-key b3453fde-ecc2-4a1f-9422-2707ddbed495 --postgres.host postgres --postgres.password autheliapw`
cmdAutheliaStorageUserIdentifiersAddShort = "Add an opaque identifier for a user to the database"
cmdAutheliaStorageUserIdentifiersAddLong = `Add an opaque identifier for a user to the database.
This subcommand allows manually adding an opaque identifier for a user to the database provided it's in the correct format.`
cmdAutheliaStorageUserIdentifiersAddExample = `authelia storage user identifiers add john --identifier f0919359-9d15-4e15-bcba-83b41620a073
authelia storage user identifiers add john --identifier f0919359-9d15-4e15-bcba-83b41620a073 --config config.yml
authelia storage user identifiers add john --identifier f0919359-9d15-4e15-bcba-83b41620a073 --encryption-key b3453fde-ecc2-4a1f-9422-2707ddbed495 --postgres.host postgres --postgres.password autheliapw`
cmdAutheliaStorageUserTOTPShort = "Manage TOTP configurations"
cmdAutheliaStorageUserTOTPLong = `Manage TOTP configurations.
This subcommand allows deleting, exporting, and creating user TOTP configurations.`
cmdAutheliaStorageUserTOTPExample = `authelia storage user totp --help`
cmdAutheliaStorageUserTOTPGenerateShort = "Generate a TOTP configuration for a user"
cmdAutheliaStorageUserTOTPGenerateLong = `Generate a TOTP configuration for a user.
This subcommand allows generating a new TOTP configuration for a user,
and overwriting the existing configuration if applicable.`
cmdAutheliaStorageUserTOTPGenerateExample = `authelia storage user totp generate john
authelia storage user totp generate john --period 90
authelia storage user totp generate john --digits 8
authelia storage user totp generate john --algorithm SHA512
authelia storage user totp generate john --algorithm SHA512 --config config.yml
authelia storage user totp generate john --algorithm SHA512 --config config.yml --path john.png`
cmdAutheliaStorageUserTOTPDeleteShort = "Delete a TOTP configuration for a user"
cmdAutheliaStorageUserTOTPDeleteLong = `Delete a TOTP configuration for a user.
This subcommand allows deleting a TOTP configuration directly from the database for a given user.`
cmdAutheliaStorageUserTOTPDeleteExample = `authelia storage user totp delete john
authelia storage user totp delete john --config config.yml
authelia storage user totp delete john --encryption-key b3453fde-ecc2-4a1f-9422-2707ddbed495 --postgres.host postgres --postgres.password autheliapw`
cmdAutheliaStorageUserTOTPExportShort = "Perform exports of the TOTP configurations"
cmdAutheliaStorageUserTOTPExportLong = `Perform exports of the TOTP configurations.
This subcommand allows exporting TOTP configurations to various formats.`
cmdAutheliaStorageUserTOTPExportExample = `authelia storage user totp export --format csv
authelia storage user totp export --format png --dir ./totp-qr
authelia storage user totp export --format png --dir ./totp-qr --config config.yml
authelia storage user totp export --format png --dir ./totp-qr --encryption-key b3453fde-ecc2-4a1f-9422-2707ddbed495 --postgres.host postgres --postgres.password autheliapw`
cmdAutheliaStorageSchemaInfoShort = "Show the storage information"
cmdAutheliaStorageSchemaInfoLong = `Show the storage information.
This subcommand shows advanced information about the storage schema useful in some diagnostic tasks.`
cmdAutheliaStorageSchemaInfoExample = `authelia storage schema-info
authelia storage schema-info --config config.yml
authelia storage schema-info --encryption-key b3453fde-ecc2-4a1f-9422-2707ddbed495 --postgres.host postgres --postgres.password autheliapw`
cmdAutheliaStorageMigrateShort = "Perform or list migrations"
cmdAutheliaStorageMigrateLong = `Perform or list migrations.
This subcommand handles schema migration tasks.`
cmdAutheliaStorageMigrateExample = `authelia storage migrate --help`
cmdAutheliaStorageMigrateHistoryShort = "Show migration history"
cmdAutheliaStorageMigrateHistoryLong = `Show migration history.
This subcommand allows users to list previous migrations.`
cmdAutheliaStorageMigrateHistoryExample = `authelia storage migrate history
authelia storage migrate history --config config.yml
authelia storage migrate history --encryption-key b3453fde-ecc2-4a1f-9422-2707ddbed495 --postgres.host postgres --postgres.password autheliapw`
cmdAutheliaStorageMigrateListUpShort = "List the up migrations available"
cmdAutheliaStorageMigrateListUpLong = `List the up migrations available.
This subcommand lists the schema migrations available in this version of Authelia which are greater than the current
schema version of the database.`
cmdAutheliaStorageMigrateListUpExample = `authelia storage migrate list-up
authelia storage migrate list-up --config config.yml
authelia storage migrate list-up --encryption-key b3453fde-ecc2-4a1f-9422-2707ddbed495 --postgres.host postgres --postgres.password autheliapw`
cmdAutheliaStorageMigrateListDownShort = "List the down migrations available"
cmdAutheliaStorageMigrateListDownLong = `List the down migrations available.
This subcommand lists the schema migrations available in this version of Authelia which are less than the current
schema version of the database.`
cmdAutheliaStorageMigrateListDownExample = `authelia storage migrate list-down
authelia storage migrate list-down --config config.yml
authelia storage migrate list-down --encryption-key b3453fde-ecc2-4a1f-9422-2707ddbed495 --postgres.host postgres --postgres.password autheliapw`
cmdAutheliaStorageMigrateUpShort = "Perform a migration up"
cmdAutheliaStorageMigrateUpLong = `Perform a migration up.
This subcommand performs the schema migrations available in this version of Authelia which are greater than the current
schema version of the database. By default this will migrate up to the latest available, but you can customize this.`
cmdAutheliaStorageMigrateUpExample = `authelia storage migrate up
authelia storage migrate up --config config.yml
authelia storage migrate up --target 20 --config config.yml
authelia storage migrate up --encryption-key b3453fde-ecc2-4a1f-9422-2707ddbed495 --postgres.host postgres --postgres.password autheliapw`
cmdAutheliaStorageMigrateDownShort = "Perform a migration down"
cmdAutheliaStorageMigrateDownLong = `Perform a migration down.
This subcommand performs the schema migrations available in this version of Authelia which are less than the current
schema version of the database.`
cmdAutheliaStorageMigrateDownExample = `authelia storage migrate down --target 20
authelia storage migrate down --target 20 --config config.yml
authelia storage migrate down --target 20 --encryption-key b3453fde-ecc2-4a1f-9422-2707ddbed495 --postgres.host postgres --postgres.password autheliapw`
cmdAutheliaValidateConfigShort = "Check a configuration against the internal configuration validation mechanisms"
cmdAutheliaValidateConfigLong = `Check a configuration against the internal configuration validation mechanisms.
This subcommand allows validation of the YAML and Environment configurations so that a configuration can be checked
prior to deploying it.`
cmdAutheliaValidateConfigExample = `authelia validate-config
authelia validate-config --config config.yml`
cmdAutheliaCertificatesShort = "Commands related to certificate generation"
cmdAutheliaCertificatesLong = `Commands related to certificate generation.
This subcommand allows preforming X509 certificate tasks.`
cmdAutheliaCertificatesExample = `authelia certificates --help`
cmdAutheliaCertificatesGenerateShort = "Generate a self-signed certificate"
cmdAutheliaCertificatesGenerateLong = `Generate a self-signed certificate.
This subcommand allows generating self-signed certificates.`
cmdAutheliaCertificatesGenerateExample = `authelia certificates generate
authelia certificates generate --dir ./out`
cmdAutheliaRSAShort = "Commands related to rsa keypair generation"
cmdAutheliaRSALong = `Commands related to rsa keypair generation.
This subcommand allows performing RSA keypair tasks.`
cmdAutheliaRSAExample = `authelia rsa --help`
cmdAutheliaRSAGenerateShort = "Generate a RSA keypair"
cmdAutheliaRSAGenerateLong = `Generate a RSA keypair.
This subcommand allows generating an RSA keypair.`
cmdAutheliaRSAGenerateExample = `authelia rsa generate
authelia rsa generate --dir ./out`
cmdAutheliaHashPasswordShort = "Hash a password to be used in file-based users database."
cmdAutheliaHashPasswordLong = `Hash a password to be used in file-based users database.`
//nolint:gosec // This is an example.
cmdAutheliaHashPasswordExample = `authelia hash-password -- 'mypass'
authelia hash-password --sha512 -- 'mypass'
authelia hash-password --iterations=4 -- 'mypass'
authelia hash-password --memory=128 -- 'mypass'
authelia hash-password --parallelism=1 -- 'mypass'
authelia hash-password --key-length=64 -- 'mypass'`
)
const (
storageMigrateDirectionUp = "up"
storageMigrateDirectionDown = "down"
)
const (
storageExportFormatCSV = "csv"
storageExportFormatURI = "uri"
storageExportFormatPNG = "png"
storageTOTPExportFormatCSV = "csv"
storageTOTPExportFormatURI = "uri"
storageTOTPExportFormatPNG = "png"
)
var (
validStorageTOTPExportFormats = []string{storageTOTPExportFormatCSV, storageTOTPExportFormatURI, storageTOTPExportFormatPNG}
)
var (

View File

@ -12,11 +12,12 @@ import (
"github.com/authelia/authelia/v4/internal/configuration/validator"
)
// NewHashPasswordCmd returns a new Hash Password Cmd.
func NewHashPasswordCmd() (cmd *cobra.Command) {
func newHashPasswordCmd() (cmd *cobra.Command) {
cmd = &cobra.Command{
Use: "hash-password [flags] -- <password>",
Short: "Hash a password to be used in file-based users database. Default algorithm is argon2id.",
Short: cmdAutheliaHashPasswordShort,
Long: cmdAutheliaHashPasswordLong,
Example: cmdAutheliaHashPasswordExample,
Args: cobra.MinimumNArgs(1),
RunE: cmdHashPasswordRunE,
}

View File

@ -23,9 +23,9 @@ func NewRootCmd() (cmd *cobra.Command) {
cmd = &cobra.Command{
Use: "authelia",
Short: fmt.Sprintf(fmtCmdAutheliaShort, version),
Long: fmt.Sprintf(fmtCmdAutheliaLong, version),
Example: cmdAutheliaExample,
Short: fmt.Sprintf("authelia %s", version),
Long: fmt.Sprintf(fmtAutheliaLong, version),
Version: version,
Args: cobra.NoArgs,
PreRun: newCmdWithConfigPreRun(true, true, true),
@ -36,11 +36,10 @@ func NewRootCmd() (cmd *cobra.Command) {
cmd.AddCommand(
newBuildInfoCmd(),
NewCertificatesCmd(),
newCompletionCmd(),
NewHashPasswordCmd(),
newCertificatesCmd(),
newHashPasswordCmd(),
NewRSACmd(),
NewStorageCmd(),
newStorageCmd(),
newValidateConfigCmd(),
newAccessControlCommand(),
)

View File

@ -14,7 +14,9 @@ import (
func NewRSACmd() (cmd *cobra.Command) {
cmd = &cobra.Command{
Use: "rsa",
Short: "Commands related to rsa keypair generation",
Short: cmdAutheliaRSAShort,
Long: cmdAutheliaRSALong,
Example: cmdAutheliaRSAExample,
Args: cobra.NoArgs,
}
@ -26,7 +28,9 @@ func NewRSACmd() (cmd *cobra.Command) {
func newRSAGenerateCmd() (cmd *cobra.Command) {
cmd = &cobra.Command{
Use: "generate",
Short: "Generate a RSA keypair",
Short: cmdAutheliaRSAGenerateShort,
Long: cmdAutheliaRSAGenerateLong,
Example: cmdAutheliaRSAGenerateExample,
Args: cobra.NoArgs,
Run: cmdRSAGenerateRun,
}

View File

@ -1,16 +1,20 @@
package commands
import (
"fmt"
"strings"
"github.com/spf13/cobra"
"github.com/authelia/authelia/v4/internal/configuration/schema"
)
// NewStorageCmd returns a new storage *cobra.Command.
func NewStorageCmd() (cmd *cobra.Command) {
func newStorageCmd() (cmd *cobra.Command) {
cmd = &cobra.Command{
Use: "storage",
Short: "Manage the Authelia storage",
Short: cmdAutheliaStorageShort,
Long: cmdAutheliaStorageLong,
Example: cmdAutheliaStorageExample,
Args: cobra.NoArgs,
PersistentPreRunE: storagePersistentPreRunE,
}
@ -48,15 +52,61 @@ func NewStorageCmd() (cmd *cobra.Command) {
return cmd
}
func newStorageEncryptionCmd() (cmd *cobra.Command) {
cmd = &cobra.Command{
Use: "encryption",
Short: cmdAutheliaStorageEncryptionShort,
Long: cmdAutheliaStorageEncryptionLong,
Example: cmdAutheliaStorageEncryptionExample,
}
cmd.AddCommand(
newStorageEncryptionChangeKeyCmd(),
newStorageEncryptionCheckCmd(),
)
return cmd
}
func newStorageEncryptionCheckCmd() (cmd *cobra.Command) {
cmd = &cobra.Command{
Use: "check",
Short: cmdAutheliaStorageEncryptionCheckShort,
Long: cmdAutheliaStorageEncryptionCheckLong,
Example: cmdAutheliaStorageEncryptionCheckExample,
RunE: storageSchemaEncryptionCheckRunE,
}
cmd.Flags().Bool("verbose", false, "enables verbose checking of every row of encrypted data")
return cmd
}
func newStorageEncryptionChangeKeyCmd() (cmd *cobra.Command) {
cmd = &cobra.Command{
Use: "change-key",
Short: cmdAutheliaStorageEncryptionChangeKeyShort,
Long: cmdAutheliaStorageEncryptionChangeKeyLong,
Example: cmdAutheliaStorageEncryptionChangeKeyExample,
RunE: storageSchemaEncryptionChangeKeyRunE,
}
cmd.Flags().String("new-encryption-key", "", "the new key to encrypt the data with")
return cmd
}
func newStorageUserCmd() (cmd *cobra.Command) {
cmd = &cobra.Command{
Use: "user",
Short: "Manages user settings",
Short: cmdAutheliaStorageUserShort,
Long: cmdAutheliaStorageUserLong,
Example: cmdAutheliaStorageUserExample,
}
cmd.AddCommand(
newStorageUserIdentifiersCmd(),
newStorageTOTPCmd(),
newStorageUserTOTPCmd(),
)
return cmd
@ -65,7 +115,9 @@ func newStorageUserCmd() (cmd *cobra.Command) {
func newStorageUserIdentifiersCmd() (cmd *cobra.Command) {
cmd = &cobra.Command{
Use: "identifiers",
Short: "Manages user opaque identifiers",
Short: cmdAutheliaStorageUserIdentifiersShort,
Long: cmdAutheliaStorageUserIdentifiersLong,
Example: cmdAutheliaStorageUserIdentifiersExample,
}
cmd.AddCommand(
@ -81,7 +133,9 @@ func newStorageUserIdentifiersCmd() (cmd *cobra.Command) {
func newStorageUserIdentifiersExportCmd() (cmd *cobra.Command) {
cmd = &cobra.Command{
Use: "export",
Short: "Export the identifiers to a YAML file",
Short: cmdAutheliaStorageUserIdentifiersExportShort,
Long: cmdAutheliaStorageUserIdentifiersExportLong,
Example: cmdAutheliaStorageUserIdentifiersExportExample,
RunE: storageUserIdentifiersExport,
}
@ -93,7 +147,9 @@ func newStorageUserIdentifiersExportCmd() (cmd *cobra.Command) {
func newStorageUserIdentifiersImportCmd() (cmd *cobra.Command) {
cmd = &cobra.Command{
Use: "import",
Short: "Import the identifiers from a YAML file",
Short: cmdAutheliaStorageUserIdentifiersImportShort,
Long: cmdAutheliaStorageUserIdentifiersImportLong,
Example: cmdAutheliaStorageUserIdentifiersImportExample,
RunE: storageUserIdentifiersImport,
}
@ -105,12 +161,14 @@ func newStorageUserIdentifiersImportCmd() (cmd *cobra.Command) {
func newStorageUserIdentifiersGenerateCmd() (cmd *cobra.Command) {
cmd = &cobra.Command{
Use: "generate",
Short: "Generate opaque identifiers in bulk",
Short: cmdAutheliaStorageUserIdentifiersGenerateShort,
Long: cmdAutheliaStorageUserIdentifiersGenerateLong,
Example: cmdAutheliaStorageUserIdentifiersGenerateExample,
RunE: storageUserIdentifiersGenerate,
}
cmd.Flags().StringSlice("users", nil, "The list of users to generate the opaque identifiers for")
cmd.Flags().StringSlice("services", []string{identifierServiceOpenIDConnect}, "The list of services to generate the opaque identifiers for, valid values are: openid")
cmd.Flags().StringSlice("services", []string{identifierServiceOpenIDConnect}, fmt.Sprintf("The list of services to generate the opaque identifiers for, valid values are: %s", strings.Join(validIdentifierServices, ", ")))
cmd.Flags().StringSlice("sectors", []string{""}, "The list of sectors to generate identifiers for")
return cmd
@ -118,76 +176,44 @@ func newStorageUserIdentifiersGenerateCmd() (cmd *cobra.Command) {
func newStorageUserIdentifiersAddCmd() (cmd *cobra.Command) {
cmd = &cobra.Command{
Use: "add [username]",
Short: "Add an identifiers to the database",
Use: "add <username>",
Short: cmdAutheliaStorageUserIdentifiersAddShort,
Long: cmdAutheliaStorageUserIdentifiersAddLong,
Example: cmdAutheliaStorageUserIdentifiersAddExample,
Args: cobra.ExactArgs(1),
RunE: storageUserIdentifiersAdd,
}
cmd.Flags().String("identifier", "", "The optional version 4 UUID to use, if not set a random one will be used")
cmd.Flags().String("service", identifierServiceOpenIDConnect, "The service to add the identifier for, valid values are: openid")
cmd.Flags().String("service", identifierServiceOpenIDConnect, fmt.Sprintf("The service to add the identifier for, valid values are: %s", strings.Join(validIdentifierServices, ", ")))
cmd.Flags().String("sector", "", "The sector identifier to use (should usually be blank)")
return cmd
}
func newStorageEncryptionCmd() (cmd *cobra.Command) {
cmd = &cobra.Command{
Use: "encryption",
Short: "Manages encryption",
}
cmd.AddCommand(
newStorageEncryptionChangeKeyCmd(),
newStorageEncryptionCheckCmd(),
)
return cmd
}
func newStorageEncryptionCheckCmd() (cmd *cobra.Command) {
cmd = &cobra.Command{
Use: "check",
Short: "Checks the encryption key against the database data",
RunE: storageSchemaEncryptionCheckRunE,
}
cmd.Flags().Bool("verbose", false, "enables verbose checking of every row of encrypted data")
return cmd
}
func newStorageEncryptionChangeKeyCmd() (cmd *cobra.Command) {
cmd = &cobra.Command{
Use: "change-key",
Short: "Changes the encryption key",
RunE: storageSchemaEncryptionChangeKeyRunE,
}
cmd.Flags().String("new-encryption-key", "", "the new key to encrypt the data with")
return cmd
}
func newStorageTOTPCmd() (cmd *cobra.Command) {
func newStorageUserTOTPCmd() (cmd *cobra.Command) {
cmd = &cobra.Command{
Use: "totp",
Short: "Manage TOTP configurations",
Short: cmdAutheliaStorageUserTOTPShort,
Long: cmdAutheliaStorageUserTOTPLong,
Example: cmdAutheliaStorageUserTOTPExample,
}
cmd.AddCommand(
newStorageTOTPGenerateCmd(),
newStorageTOTPDeleteCmd(),
newStorageTOTPExportCmd(),
newStorageUserTOTPGenerateCmd(),
newStorageUserTOTPDeleteCmd(),
newStorageUserTOTPExportCmd(),
)
return cmd
}
func newStorageTOTPGenerateCmd() (cmd *cobra.Command) {
func newStorageUserTOTPGenerateCmd() (cmd *cobra.Command) {
cmd = &cobra.Command{
Use: "generate [username]",
Short: "Generate a TOTP configuration for a user",
Use: "generate <username>",
Short: cmdAutheliaStorageUserTOTPGenerateShort,
Long: cmdAutheliaStorageUserTOTPGenerateLong,
Example: cmdAutheliaStorageUserTOTPGenerateExample,
RunE: storageTOTPGenerateRunE,
Args: cobra.ExactArgs(1),
}
@ -204,10 +230,12 @@ func newStorageTOTPGenerateCmd() (cmd *cobra.Command) {
return cmd
}
func newStorageTOTPDeleteCmd() (cmd *cobra.Command) {
func newStorageUserTOTPDeleteCmd() (cmd *cobra.Command) {
cmd = &cobra.Command{
Use: "delete username",
Short: "Delete a TOTP configuration for a user",
Use: "delete <username>",
Short: cmdAutheliaStorageUserTOTPDeleteShort,
Long: cmdAutheliaStorageUserTOTPDeleteLong,
Example: cmdAutheliaStorageUserTOTPDeleteExample,
RunE: storageTOTPDeleteRunE,
Args: cobra.ExactArgs(1),
}
@ -215,14 +243,16 @@ func newStorageTOTPDeleteCmd() (cmd *cobra.Command) {
return cmd
}
func newStorageTOTPExportCmd() (cmd *cobra.Command) {
func newStorageUserTOTPExportCmd() (cmd *cobra.Command) {
cmd = &cobra.Command{
Use: "export",
Short: "Performs exports of the TOTP configurations",
Short: cmdAutheliaStorageUserTOTPExportShort,
Long: cmdAutheliaStorageUserTOTPExportLong,
Example: cmdAutheliaStorageUserTOTPExportExample,
RunE: storageTOTPExportRunE,
}
cmd.Flags().String("format", storageExportFormatURI, "sets the output format")
cmd.Flags().String("format", storageTOTPExportFormatURI, fmt.Sprintf("sets the output format, valid values are: %s", strings.Join(validStorageTOTPExportFormats, ", ")))
cmd.Flags().String("dir", "", "used with the png output format to specify which new directory to save the files in")
return cmd
@ -231,7 +261,9 @@ func newStorageTOTPExportCmd() (cmd *cobra.Command) {
func newStorageSchemaInfoCmd() (cmd *cobra.Command) {
cmd = &cobra.Command{
Use: "schema-info",
Short: "Show the storage information",
Short: cmdAutheliaStorageSchemaInfoShort,
Long: cmdAutheliaStorageSchemaInfoLong,
Example: cmdAutheliaStorageSchemaInfoExample,
RunE: storageSchemaInfoRunE,
}
@ -242,7 +274,9 @@ func newStorageSchemaInfoCmd() (cmd *cobra.Command) {
func newStorageMigrateCmd() (cmd *cobra.Command) {
cmd = &cobra.Command{
Use: "migrate",
Short: "Perform or list migrations",
Short: cmdAutheliaStorageMigrateShort,
Long: cmdAutheliaStorageMigrateLong,
Example: cmdAutheliaStorageMigrateExample,
Args: cobra.NoArgs,
}
@ -258,7 +292,9 @@ func newStorageMigrateCmd() (cmd *cobra.Command) {
func newStorageMigrateHistoryCmd() (cmd *cobra.Command) {
cmd = &cobra.Command{
Use: "history",
Short: "Show migration history",
Short: cmdAutheliaStorageMigrateHistoryShort,
Long: cmdAutheliaStorageMigrateHistoryLong,
Example: cmdAutheliaStorageMigrateHistoryExample,
Args: cobra.NoArgs,
RunE: storageMigrateHistoryRunE,
}
@ -269,7 +305,9 @@ func newStorageMigrateHistoryCmd() (cmd *cobra.Command) {
func newStorageMigrateListUpCmd() (cmd *cobra.Command) {
cmd = &cobra.Command{
Use: "list-up",
Short: "List the up migrations available",
Short: cmdAutheliaStorageMigrateListUpShort,
Long: cmdAutheliaStorageMigrateListUpLong,
Example: cmdAutheliaStorageMigrateListUpExample,
Args: cobra.NoArgs,
RunE: newStorageMigrateListRunE(true),
}
@ -280,7 +318,9 @@ func newStorageMigrateListUpCmd() (cmd *cobra.Command) {
func newStorageMigrateListDownCmd() (cmd *cobra.Command) {
cmd = &cobra.Command{
Use: "list-down",
Short: "List the down migrations available",
Short: cmdAutheliaStorageMigrateListDownShort,
Long: cmdAutheliaStorageMigrateListDownLong,
Example: cmdAutheliaStorageMigrateListDownExample,
Args: cobra.NoArgs,
RunE: newStorageMigrateListRunE(false),
}
@ -291,7 +331,9 @@ func newStorageMigrateListDownCmd() (cmd *cobra.Command) {
func newStorageMigrateUpCmd() (cmd *cobra.Command) {
cmd = &cobra.Command{
Use: storageMigrateDirectionUp,
Short: "Perform a migration up",
Short: cmdAutheliaStorageMigrateUpShort,
Long: cmdAutheliaStorageMigrateUpLong,
Example: cmdAutheliaStorageMigrateUpExample,
Args: cobra.NoArgs,
RunE: newStorageMigrationRunE(true),
}
@ -304,7 +346,9 @@ func newStorageMigrateUpCmd() (cmd *cobra.Command) {
func newStorageMigrateDownCmd() (cmd *cobra.Command) {
cmd = &cobra.Command{
Use: storageMigrateDirectionDown,
Short: "Perform a migration down",
Short: cmdAutheliaStorageMigrateDownShort,
Long: cmdAutheliaStorageMigrateDownLong,
Example: cmdAutheliaStorageMigrateDownExample,
Args: cobra.NoArgs,
RunE: newStorageMigrationRunE(false),
}

View File

@ -351,17 +351,17 @@ func storageTOTPExportRunE(cmd *cobra.Command, args []string) (err error) {
return err
}
if page == 0 && format == storageExportFormatCSV {
if page == 0 && format == storageTOTPExportFormatCSV {
fmt.Printf("issuer,username,algorithm,digits,period,secret\n")
}
for _, c := range configurations {
switch format {
case storageExportFormatCSV:
case storageTOTPExportFormatCSV:
fmt.Printf("%s,%s,%s,%d,%d,%s\n", c.Issuer, c.Username, c.Algorithm, c.Digits, c.Period, string(c.Secret))
case storageExportFormatURI:
case storageTOTPExportFormatURI:
fmt.Println(c.URI())
case storageExportFormatPNG:
case storageTOTPExportFormatPNG:
file, _ := os.Create(filepath.Join(dir, fmt.Sprintf("%s.png", c.Username)))
if img, err = c.Image(256, 256); err != nil {
@ -385,7 +385,7 @@ func storageTOTPExportRunE(cmd *cobra.Command, args []string) (err error) {
}
}
if format == storageExportFormatPNG {
if format == storageTOTPExportFormatPNG {
fmt.Printf("Exported TOTP QR codes in PNG format in the '%s' directory\n", dir)
}
@ -402,9 +402,9 @@ func storageTOTPExportGetConfigFromFlags(cmd *cobra.Command) (format, dir string
}
switch format {
case storageExportFormatCSV, storageExportFormatURI:
case storageTOTPExportFormatCSV, storageTOTPExportFormatURI:
break
case storageExportFormatPNG:
case storageTOTPExportFormatPNG:
if dir == "" {
dir = utils.RandomString(8, utils.AlphaNumericCharacters, false)
}

View File

@ -11,7 +11,9 @@ import (
func newValidateConfigCmd() (cmd *cobra.Command) {
cmd = &cobra.Command{
Use: "validate-config",
Short: "Check a configuration against the internal configuration validation mechanisms",
Short: cmdAutheliaValidateConfigShort,
Long: cmdAutheliaValidateConfigLong,
Example: cmdAutheliaValidateConfigExample,
Args: cobra.NoArgs,
RunE: cmdValidateConfigRunE,
}

View File

@ -1,12 +1,12 @@
// Code generated by go generate. DO NOT EDIT.
//
// Run the following command to generate this file:
// go run ./cmd/authelia-scripts gen
// go run ./cmd/authelia-gen code keys
//
package schema
// Keys represents the detected schema keys.
// Keys is a list of valid schema keys detected by reflecting over a schema.Configuration struct.
var Keys = []string{
"theme",
"certificates_directory",