Passed
Pull Request — master (#1086)
by Tolga
02:29
created

cmd.HideSecrets   A

Complexity

Conditions 2

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 4
nop 1
dl 0
loc 6
rs 10
c 0
b 0
f 0
1
package cmd
2
3
import (
4
	"fmt"
5
	"os"
6
	"strings"
7
8
	"github.com/Permify/permify/internal/config"
9
	"github.com/Permify/permify/pkg/cmd/flags"
10
11
	"github.com/gookit/color"
12
	"github.com/olekukonko/tablewriter"
13
	"github.com/spf13/cobra"
14
	"github.com/spf13/viper"
15
)
16
17
func NewConfigCommand() *cobra.Command {
18
	cmd := &cobra.Command{
19
		Use:   "config",
20
		Short: "Inspect permify configuration and environment variables",
21
		RunE:  conf(),
22
		Args:  cobra.NoArgs,
23
	}
24
25
	flags.RegisterServeFlags(cmd)
26
27
	return cmd
28
}
29
30
func conf() func(cmd *cobra.Command, args []string) error {
31
	return func(cmd *cobra.Command, args []string) error {
32
		var cfg *config.Config
33
		var err error
34
		cfgFile := viper.GetString("config.file")
35
		if cfgFile != "" {
36
			cfg, err = config.NewConfigWithFile(cfgFile)
37
			if err != nil {
38
				return fmt.Errorf("failed to create new config: %w", err)
0 ignored issues
show
introduced by
unrecognized printf verb 'w'
Loading history...
39
			}
40
41
			if err = viper.Unmarshal(cfg); err != nil {
42
				return fmt.Errorf("failed to unmarshal config: %w", err)
0 ignored issues
show
introduced by
unrecognized printf verb 'w'
Loading history...
43
			}
44
		} else {
45
			// Load configuration
46
			cfg, err = config.NewConfig()
47
			if err != nil {
48
				return fmt.Errorf("failed to create new config: %w", err)
0 ignored issues
show
introduced by
unrecognized printf verb 'w'
Loading history...
49
			}
50
51
			if err = viper.Unmarshal(cfg); err != nil {
52
				return fmt.Errorf("failed to unmarshal config: %w", err)
0 ignored issues
show
introduced by
unrecognized printf verb 'w'
Loading history...
53
			}
54
		}
55
56
		var data [][]string
57
58
		data = append(data,
59
			[]string{"account_id", cfg.AccountID, getKeyOrigin(cmd, "account-id", "PERMIFY_ACCOUNT_ID")},
60
			// SERVER
61
			[]string{"server.rate_limit", fmt.Sprintf("%v", cfg.Server.RateLimit), getKeyOrigin(cmd, "server-rate-limit", "PERMIFY_RATE_LIMIT")},
62
			[]string{"server.grpc.port", cfg.Server.GRPC.Port, getKeyOrigin(cmd, "grpc-port", "PERMIFY_GRPC_PORT")},
63
			[]string{"server.grpc.tls.enabled", fmt.Sprintf("%v", cfg.Server.GRPC.TLSConfig.Enabled), getKeyOrigin(cmd, "grpc-tls-enabled", "PERMIFY_GRPC_TLS_ENABLED")},
64
			[]string{"server.grpc.tls.cert", cfg.Server.GRPC.TLSConfig.CertPath, getKeyOrigin(cmd, "grpc-tls-cert-path", "PERMIFY_GRPC_TLS_CERT_PATH")},
65
			[]string{"server.http.enabled", fmt.Sprintf("%v", cfg.Server.HTTP.Enabled), getKeyOrigin(cmd, "http-enabled", "PERMIFY_HTTP_ENABLED")},
66
			[]string{"server.http.tls.enabled", fmt.Sprintf("%v", cfg.Server.HTTP.TLSConfig.Enabled), getKeyOrigin(cmd, "http-tls-enabled", "PERMIFY_HTTP_TLS_ENABLED")},
67
			[]string{"server.http.tls.key", HideSecret(cfg.Server.HTTP.TLSConfig.KeyPath), getKeyOrigin(cmd, "http-tls-key-path", "PERMIFY_HTTP_TLS_KEY_PATH")},
68
			[]string{"server.http.tls.cert", HideSecret(cfg.Server.HTTP.TLSConfig.CertPath), getKeyOrigin(cmd, "http-tls-cert-path", "PERMIFY_HTTP_TLS_CERT_PATH")},
69
			[]string{"server.http.cors_allowed_origins", fmt.Sprintf("%v", cfg.Server.HTTP.CORSAllowedOrigins), getKeyOrigin(cmd, "http-cors-allowed-origins", "PERMIFY_HTTP_CORS_ALLOWED_ORIGINS")},
70
			[]string{"server.http.cors_allowed_headers", fmt.Sprintf("%v", cfg.Server.HTTP.CORSAllowedHeaders), getKeyOrigin(cmd, "http-cors-allowed-headers", "PERMIFY_HTTP_CORS_ALLOWED_HEADERS")},
71
			// PROFILER
72
			[]string{"profiler.enabled", fmt.Sprintf("%v", cfg.Profiler.Enabled), getKeyOrigin(cmd, "profiler-enabled", "PERMIFY_PROFILER_ENABLED")},
73
			[]string{"profiler.port", cfg.Profiler.Port, getKeyOrigin(cmd, "profiler-port", "PERMIFY_PROFILER_PORT")},
74
			// LOG
75
			[]string{"logger.level", cfg.Log.Level, getKeyOrigin(cmd, "log-level", "PERMIFY_LOG_LEVEL")},
76
			[]string{"logger.output", cfg.Log.Level, getKeyOrigin(cmd, "log-output", "PERMIFY_LOG_OUTPUT")},
77
			// AUTHN
78
			[]string{"authn.enabled", fmt.Sprintf("%v", cfg.Authn.Enabled), getKeyOrigin(cmd, "authn-enabled", "PERMIFY_AUTHN_ENABLED")},
79
			[]string{"authn.method", cfg.Authn.Method, getKeyOrigin(cmd, "authn-method", "PERMIFY_AUTHN_METHOD")},
80
			[]string{"authn.preshared.keys", fmt.Sprintf("%v", HideSecrets(cfg.Authn.Preshared.Keys...)), getKeyOrigin(cmd, "authn-preshared-keys", "PERMIFY_AUTHN_PRESHARED_KEYS")},
81
			[]string{"authn.oidc.issuer", HideSecret(cfg.Authn.Oidc.Issuer), getKeyOrigin(cmd, "authn-oidc-issuer", "PERMIFY_AUTHN_OIDC_ISSUER")},
82
			[]string{"authn.oidc.audience", HideSecret(cfg.Authn.Oidc.Audience), getKeyOrigin(cmd, "authn-oidc-audience", "PERMIFY_AUTHN_OIDC_AUDIENCE")},
83
			// TRACER
84
			[]string{"tracer.enabled", fmt.Sprintf("%v", cfg.Tracer.Enabled), getKeyOrigin(cmd, "tracer-enabled", "PERMIFY_TRACER_ENABLED")},
85
			[]string{"tracer.exporter", cfg.Tracer.Exporter, getKeyOrigin(cmd, "tracer-exporter", "PERMIFY_TRACER_EXPORTER")},
86
			[]string{"tracer.endpoint", HideSecret(cfg.Tracer.Exporter), getKeyOrigin(cmd, "tracer-endpoint", "PERMIFY_TRACER_ENDPOINT")},
87
			[]string{"tracer.insecure", fmt.Sprintf("%v", cfg.Tracer.Insecure), getKeyOrigin(cmd, "tracer-insecure", "PERMIFY_TRACER_INSECURE")},
88
			[]string{"tracer.urlpath", cfg.Tracer.URLPath, getKeyOrigin(cmd, "tracer-urlpath", "PERMIFY_TRACER_URL_PATH")},
89
			// METER
90
			[]string{"meter.enabled", fmt.Sprintf("%v", cfg.Meter.Enabled), getKeyOrigin(cmd, "meter-enabled", "PERMIFY_METER_ENABLED")},
91
			[]string{"meter.exporter", cfg.Meter.Exporter, getKeyOrigin(cmd, "meter-exporter", "PERMIFY_METER_EXPORTER")},
92
			[]string{"meter.endpoint", HideSecret(cfg.Meter.Exporter), getKeyOrigin(cmd, "meter-endpoint", "PERMIFY_METER_ENDPOINT")},
93
			[]string{"meter.insecure", fmt.Sprintf("%v", cfg.Meter.Insecure), getKeyOrigin(cmd, "meter-insecure", "PERMIFY_METER_INSECURE")},
94
			[]string{"meter.urlpath", cfg.Meter.URLPath, getKeyOrigin(cmd, "meter-urlpath", "PERMIFY_METER_URL_PATH")},
95
			// SERVICE
96
			[]string{"service.circuit_breaker", fmt.Sprintf("%v", cfg.Service.CircuitBreaker), getKeyOrigin(cmd, "service-circuit-breaker", "PERMIFY_SERVICE_CIRCUIT_BREAKER")},
97
			[]string{"service.schema.cache.number_of_counters", fmt.Sprintf("%v", cfg.Service.Schema.Cache.NumberOfCounters), getKeyOrigin(cmd, "service-schema-cache-number-of-counters", "PERMIFY_SERVICE_WATCH_ENABLED")},
98
			[]string{"service.schema.cache.max_cost", cfg.Service.Schema.Cache.MaxCost, getKeyOrigin(cmd, "service-schema-cache-max-cost", "PERMIFY_SERVICE_SCHEMA_CACHE_MAX_COST")},
99
			[]string{"service.permission.bulk_limit", fmt.Sprintf("%v", cfg.Service.Permission.BulkLimit), getKeyOrigin(cmd, "service-permission-bulk-limit", "PERMIFY_SERVICE_PERMISSION_BULK_LIMIT")},
100
			[]string{"service.permission.concurrency_limit", fmt.Sprintf("%v", cfg.Service.Permission.ConcurrencyLimit), getKeyOrigin(cmd, "service-permission-concurrency-limit", "PERMIFY_SERVICE_PERMISSION_CONCURRENCY_LIMIT")},
101
			[]string{"service.permission.cache.number_of_counters", fmt.Sprintf("%v", cfg.Service.Permission.Cache.NumberOfCounters), getKeyOrigin(cmd, "service-permission-cache-number-of-counters", "PERMIFY_SERVICE_PERMISSION_CACHE_NUMBER_OF_COUNTERS")},
102
			[]string{"service.permission.cache.max_cost", fmt.Sprintf("%v", cfg.Service.Permission.Cache.MaxCost), getKeyOrigin(cmd, "service-permission-cache-max-cost", "PERMIFY_SERVICE_PERMISSION_CACHE_MAX_COST")},
103
			// DATABASE
104
			[]string{"database.engine", cfg.Database.Engine, getKeyOrigin(cmd, "database-engine", "PERMIFY_DATABASE_ENGINE")},
105
			[]string{"database.uri", HideSecret(cfg.Database.URI), getKeyOrigin(cmd, "database-uri", "PERMIFY_DATABASE_URI")},
106
			[]string{"database.auto_migrate", fmt.Sprintf("%v", cfg.Database.AutoMigrate), getKeyOrigin(cmd, "database-auto-migrate", "PERMIFY_DATABASE_AUTO_MIGRATE")},
107
			[]string{"database.max_open_connections", fmt.Sprintf("%v", cfg.Database.MaxOpenConnections), getKeyOrigin(cmd, "database-max-open-connections", "PERMIFY_DATABASE_MAX_OPEN_CONNECTIONS")},
108
			[]string{"database.max_idle_connections", fmt.Sprintf("%v", cfg.Database.MaxIdleConnections), getKeyOrigin(cmd, "database-max-idle-connections", "PERMIFY_DATABASE_MAX_IDLE_CONNECTIONS")},
109
			[]string{"database.max_connection_lifetime", fmt.Sprintf("%v", cfg.Database.MaxConnectionLifetime), getKeyOrigin(cmd, "database-max-connection-lifetime", "PERMIFY_DATABASE_MAX_CONNECTION_LIFETIME")},
110
			[]string{"database.max_connection_idle_time", fmt.Sprintf("%v", cfg.Database.MaxConnectionIdleTime), getKeyOrigin(cmd, "database-max-connection-idle-time", "PERMIFY_DATABASE_MAX_CONNECTION_IDLE_TIME")},
111
			[]string{"database.garbage_collection.enabled", fmt.Sprintf("%v", cfg.Database.GarbageCollection.Enabled), getKeyOrigin(cmd, "database-garbage-collection-enabled", "PERMIFY_DATABASE_GARBAGE_COLLECTION_ENABLED")},
112
			[]string{"database.garbage_collection.interval", fmt.Sprintf("%v", cfg.Database.GarbageCollection.Interval), getKeyOrigin(cmd, "database-garbage-collection-interval", "PERMIFY_DATABASE_GARBAGE_COLLECTION_INTERVAL")},
113
			[]string{"database.garbage_collection.timeout", fmt.Sprintf("%v", cfg.Database.GarbageCollection.Timeout), getKeyOrigin(cmd, "database-garbage-collection-timeout", "PERMIFY_DATABASE_GARBAGE_COLLECTION_TIMEOUT")},
114
			[]string{"database.garbage_collection.window", fmt.Sprintf("%v", cfg.Database.GarbageCollection.Window), getKeyOrigin(cmd, "database-garbage-collection-window", "PERMIFY_DATABASE_GARBAGE_COLLECTION_WINDOW")},
115
			// DISTRIBUTED
116
			[]string{"distributed.enabled", fmt.Sprintf("%v", cfg.Distributed.Enabled), getKeyOrigin(cmd, "distributed-enabled", "PERMIFY_DISTRIBUTED_ENABLED")},
117
			[]string{"distributed.address", cfg.Distributed.Address, getKeyOrigin(cmd, "distributed-address", "PERMIFY_DISTRIBUTED_ADDRESS")},
118
			[]string{"distributed.port", cfg.Distributed.Port, getKeyOrigin(cmd, "distributed-port", "PERMIFY_DISTRIBUTED_PORT")},
119
		)
120
121
		renderConfigTable(data)
122
		return nil
123
	}
124
}
125
126
// getKeyOrigin determines the source of a configuration value.
127
// It checks whether a value was set via a command-line flag, an environment variable, or defaults to file.
128
func getKeyOrigin(cmd *cobra.Command, flagKey, envKey string) string {
129
	// Check if the command-line flag (specified by flagKey) was explicitly set by the user.
130
	if cmd.Flags().Changed(flagKey) {
131
		// If the flag was set, return "FLAG" with light green color.
132
		return color.FgLightGreen.Render("FLAG")
133
	}
134
135
	// Check if the environment variable (specified by envKey) exists.
136
	_, exists := os.LookupEnv(envKey)
137
	if exists {
138
		// If the environment variable exists, return "ENV" with light blue color.
139
		return color.FgLightBlue.Render("ENV")
140
	}
141
142
	// If neither the command-line flag nor the environment variable was set,
143
	// assume the value came from a configuration file.
144
	return color.FgYellow.Render("FILE")
145
}
146
147
// renderConfigTable displays configuration data in a formatted table on the console.
148
// It takes a 2D slice of strings where each inner slice represents a row in the table.
149
func renderConfigTable(data [][]string) {
150
	// Create a new table writer object, writing to standard output.
151
	table := tablewriter.NewWriter(os.Stdout)
152
153
	// Set the headers of the table. Each header cell is a column title.
154
	table.SetHeader([]string{"Key", "Value", "Source"})
155
156
	// Align the columns of the table: left-aligned for keys, centered for values and sources.
157
	table.SetColumnAlignment([]int{tablewriter.ALIGN_LEFT, tablewriter.ALIGN_CENTER, tablewriter.ALIGN_CENTER})
158
159
	// Set the center separator character for the table, which appears between columns.
160
	table.SetCenterSeparator("|")
161
162
	// Loop through the data and add each row to the table.
163
	for _, v := range data {
164
		table.Append(v)
165
	}
166
167
	// Render the table to standard output, displaying it to the user.
168
	table.Render()
169
}
170
171
// HideSecret replaces all but the first and last characters of a string with asterisks
172
func HideSecret(secret string) string {
173
	if len(secret) <= 2 {
174
		// If the secret is too short, just return asterisks
175
		return strings.Repeat("*", len(secret))
176
	}
177
	// Keep first and last character visible; replace the rest with asterisks
178
	return string(secret[0]) + strings.Repeat("*", len(secret)-2) + string(secret[len(secret)-1])
179
}
180
181
// HideSecrets obscures each string in a given list.
182
func HideSecrets(secrets ...string) (rv []string) {
183
	// Convert each secret to its hidden version and collect them.
184
	for _, secret := range secrets {
185
		rv = append(rv, HideSecret(secret)) // Hide each secret.
186
	}
187
	return
188
}
189