inital
This commit is contained in:
		
						commit
						e03def9d03
					
				
					 19 changed files with 1015 additions and 0 deletions
				
			
		
							
								
								
									
										2
									
								
								.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,2 @@ | |||
| test | ||||
| crtman.toml | ||||
							
								
								
									
										0
									
								
								LICENSE
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								LICENSE
									
										
									
									
									
										Normal file
									
								
							
							
								
								
									
										40
									
								
								cmd/get.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								cmd/get.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,40 @@ | |||
| /* | ||||
| Copyright © 2025 NAME HERE <EMAIL ADDRESS> | ||||
| */ | ||||
| package cmd | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 
 | ||||
| 	"github.com/spf13/cobra" | ||||
| ) | ||||
| 
 | ||||
| // getCmd represents the get command | ||||
| var getCmd = &cobra.Command{ | ||||
| 	Use:   "get", | ||||
| 	Short: "A brief description of your command", | ||||
| 	Long: `A longer description that spans multiple lines and likely contains examples | ||||
| and usage of using your command. For example: | ||||
| 
 | ||||
| Cobra is a CLI library for Go that empowers applications. | ||||
| This application is a tool to generate the needed files | ||||
| to quickly create a Cobra application.`, | ||||
| 	Run: func(cmd *cobra.Command, args []string) { | ||||
| 		fmt.Println("get called") | ||||
| 	}, | ||||
| } | ||||
| 
 | ||||
| func init() { | ||||
| 	rootCmd.AddCommand(getCmd) | ||||
| 
 | ||||
| 	// Here you will define your flags and configuration settings. | ||||
| 
 | ||||
| 	// Cobra supports Persistent Flags which will work for this command | ||||
| 	// and all subcommands, e.g.: | ||||
| 	// getCmd.PersistentFlags().String("foo", "", "A help for foo") | ||||
| 
 | ||||
| 	// Cobra supports local flags which will only run when this command | ||||
| 	// is called directly, e.g.: | ||||
| 	// getCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") | ||||
| 	getCmd.Flags().Bool("ca", false, "Get CA certificate") | ||||
| } | ||||
							
								
								
									
										48
									
								
								cmd/root.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								cmd/root.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,48 @@ | |||
| /* | ||||
| Copyright © 2025 NAME HERE <EMAIL ADDRESS> | ||||
| */ | ||||
| package cmd | ||||
| 
 | ||||
| import ( | ||||
| 	"os" | ||||
| 
 | ||||
| 	"github.com/spf13/cobra" | ||||
| 	"github.com/spf13/pflag" | ||||
| ) | ||||
| 
 | ||||
| var cfgFilePath string | ||||
| 
 | ||||
| // rootCmd represents the base command when called without any subcommands | ||||
| var rootCmd = &cobra.Command{ | ||||
| 	Use:   "crtman", | ||||
| 	Short: "A SOHO Certification System", | ||||
| 	Long:  `This Application is made for TCIVS CSE Competitors' Room.`, | ||||
| 	// Uncomment the following line if your bare application | ||||
| 	// has an action associated with it: | ||||
| 	// Run: func(cmd *cobra.Command, args []string) { }, | ||||
| } | ||||
| 
 | ||||
| // Execute adds all child commands to the root command and sets flags appropriately. | ||||
| // This is called by main.main(). It only needs to happen once to the rootCmd. | ||||
| func Execute() *pflag.FlagSet { | ||||
| 	err := rootCmd.Execute() | ||||
| 	if err != nil { | ||||
| 		os.Exit(1) | ||||
| 	} | ||||
| 	return rootCmd.Flags() | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| func init() { | ||||
| 	// Here you will define your flags and configuration settings. | ||||
| 	// Cobra supports persistent flags, which, if defined here, | ||||
| 	// will be global for your application. | ||||
| 
 | ||||
| 	rootCmd.PersistentFlags().StringVar(&cfgFilePath, "config", "", "config file (default is $HOME/.crtman.yaml)") | ||||
| 
 | ||||
| 	// Cobra also supports local flags, which will only run | ||||
| 	// when this action is called directly. | ||||
| 	rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") | ||||
| 	rootCmd.PersistentFlags().StringP("host", "c", "", "Remote/Listen Host IP") | ||||
| 	rootCmd.PersistentFlags().StringP("port", "p", "", "Remote/Listen Host Port") | ||||
| } | ||||
							
								
								
									
										89
									
								
								cmd/serve.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								cmd/serve.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,89 @@ | |||
| /* | ||||
| Copyright © 2025 NAME HERE <EMAIL ADDRESS> | ||||
| */ | ||||
| package cmd | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"log" | ||||
| 	"math/big" | ||||
| 
 | ||||
| 	"github.com/jasinco/crtman/internal/cli" | ||||
| 	"github.com/jasinco/crtman/internal/crt" | ||||
| 	"github.com/jasinco/crtman/internal/store" | ||||
| 	"github.com/jasinco/crtman/internal/web" | ||||
| 	"github.com/spf13/cobra" | ||||
| 	"github.com/spf13/viper" | ||||
| ) | ||||
| 
 | ||||
| var init_prog, reinit bool | ||||
| 
 | ||||
| // serveCmd represents the serve command | ||||
| var serveCmd = &cobra.Command{ | ||||
| 	Use:   "serve", | ||||
| 	Short: "A brief description of your command", | ||||
| 	Long: `A longer description that spans multiple lines and likely contains examples | ||||
| and usage of using your command. For example: | ||||
| 
 | ||||
| Cobra is a CLI library for Go that empowers applications. | ||||
| This application is a tool to generate the needed files | ||||
| to quickly create a Cobra application.`, | ||||
| 	Run: func(cmd *cobra.Command, args []string) { | ||||
| 		fmt.Println("serve called") | ||||
| 
 | ||||
| 		viper.BindPFlag("ca.organization", cmd.Flags().Lookup("ca_org")) | ||||
| 		viper.BindPFlag("ca.fqdn", cmd.Flags().Lookup("ca_fqdn")) | ||||
| 		viper.BindPFlags(cmd.Flags()) | ||||
| 
 | ||||
| 		// config handle | ||||
| 		err := viper.ReadInConfig() | ||||
| 		if err != nil { | ||||
| 			log.Printf("can't load config %e\n", err) | ||||
| 		} | ||||
| 		cli.GetListen() | ||||
| 		cli.GetPersistent() | ||||
| 		log.Printf("Get Persistent Path: %s", cli.PersistentPath) | ||||
| 		store.InitStore(cli.PersistentPath) | ||||
| 		defer store.CloseStore() | ||||
| 
 | ||||
| 		check_init := store.ChekInit() | ||||
| 
 | ||||
| 		if init_prog { | ||||
| 			if check_init && !reinit { | ||||
| 				log.Fatalln("you have initiazlized the program.") | ||||
| 			} | ||||
| 			cli.GetCA_CFG() | ||||
| 			crt.IssueRoot(cli.CA_config) | ||||
| 			store.Serial = *big.NewInt(1) | ||||
| 			store.InitStoreData() | ||||
| 			check_init = true | ||||
| 		} | ||||
| 
 | ||||
| 		// Check Root CA | ||||
| 		if !check_init { | ||||
| 			log.Fatalln("Hasn't Initialized, CA_cert not found") | ||||
| 		} | ||||
| 		cli.ParseFederation() | ||||
| 
 | ||||
| 		web.Listen(cli.Host, cli.Port) | ||||
| 	}, | ||||
| } | ||||
| 
 | ||||
| func init() { | ||||
| 	rootCmd.AddCommand(serveCmd) | ||||
| 
 | ||||
| 	// Here you will define your flags and configuration settings. | ||||
| 
 | ||||
| 	// Cobra supports Persistent Flags which will work for this command | ||||
| 	// and all subcommands, e.g.: | ||||
| 	// serveCmd.PersistentFlags().String("foo", "", "A help for foo") | ||||
| 
 | ||||
| 	// Cobra supports local flags which will only run when this command | ||||
| 	// is called directly, e.g.: | ||||
| 	// serveCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") | ||||
| 	serveCmd.Flags().StringP("store", "s", "", "The place the appstore its persistent stuff") | ||||
| 	serveCmd.Flags().BoolVar(&init_prog, "init", false, "Init the settings, create CA, ...etc") | ||||
| 	serveCmd.Flags().BoolVar(&reinit, "reinit", false, "It reinit the whole chain(ca, keys)") | ||||
| 	serveCmd.Flags().String("ca_org", "", "the CA's organization") | ||||
| 	serveCmd.Flags().String("ca_fqdn", "", "the CA's FQDN") | ||||
| } | ||||
							
								
								
									
										22
									
								
								crtman.template.toml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								crtman.template.toml
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,22 @@ | |||
| store="./test.db`" | ||||
| 
 | ||||
| [ca] | ||||
| organization="" | ||||
| fqdn="" | ||||
| 
 | ||||
| [federation] | ||||
| backend="ldap" | ||||
| auth=["ldap"] | ||||
| 
 | ||||
| # this ldap config is for LLDAP | ||||
| [federation.ldap] | ||||
| auth_user = "uid=admin,ou=people,dc=tcivs,dc=intra" | ||||
| auth_password= "" | ||||
| base_url="ldap://localhost:3890" | ||||
| # user | ||||
| user_identify = "uid" | ||||
| user_objectclass = "persons" | ||||
| #group | ||||
| group_identify = "cn" | ||||
| group_objectclass = "groups" | ||||
| group_base_dn = "ou=group,dc=tcivs,dc=intra" | ||||
							
								
								
									
										65
									
								
								go.mod
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								go.mod
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,65 @@ | |||
| module github.com/jasinco/crtman | ||||
| 
 | ||||
| go 1.25.1 | ||||
| 
 | ||||
| require ( | ||||
| 	github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect | ||||
| 	github.com/abcum/lcp v0.0.0-20201209214815-7a3f3840be81 // indirect | ||||
| 	github.com/bytedance/sonic v1.14.0 // indirect | ||||
| 	github.com/bytedance/sonic/loader v0.3.0 // indirect | ||||
| 	github.com/cloudwego/base64x v0.1.6 // indirect | ||||
| 	github.com/fsnotify/fsnotify v1.9.0 // indirect | ||||
| 	github.com/gabriel-vasile/mimetype v1.4.8 // indirect | ||||
| 	github.com/gin-contrib/sse v1.1.0 // indirect | ||||
| 	github.com/gin-gonic/gin v1.11.0 // indirect | ||||
| 	github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 // indirect | ||||
| 	github.com/go-ldap/ldap/v3 v3.4.12 // indirect | ||||
| 	github.com/go-playground/locales v0.14.1 // indirect | ||||
| 	github.com/go-playground/universal-translator v0.18.1 // indirect | ||||
| 	github.com/go-playground/validator/v10 v10.27.0 // indirect | ||||
| 	github.com/go-viper/mapstructure/v2 v2.4.0 // indirect | ||||
| 	github.com/goccy/go-json v0.10.2 // indirect | ||||
| 	github.com/goccy/go-yaml v1.18.0 // indirect | ||||
| 	github.com/gofrs/flock v0.8.1 // indirect | ||||
| 	github.com/google/uuid v1.6.0 // indirect | ||||
| 	github.com/hashicorp/go-immutable-radix/v2 v2.0.0 // indirect | ||||
| 	github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect | ||||
| 	github.com/inconshreveable/mousetrap v1.1.0 // indirect | ||||
| 	github.com/joho/godotenv v1.5.1 // indirect | ||||
| 	github.com/json-iterator/go v1.1.12 // indirect | ||||
| 	github.com/klauspost/cpuid/v2 v2.3.0 // indirect | ||||
| 	github.com/leodido/go-urn v1.4.0 // indirect | ||||
| 	github.com/mattetti/filebuffer v1.0.1 // indirect | ||||
| 	github.com/mattn/go-isatty v0.0.20 // indirect | ||||
| 	github.com/mattn/go-sqlite3 v1.14.32 // indirect | ||||
| 	github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect | ||||
| 	github.com/modern-go/reflect2 v1.0.2 // indirect | ||||
| 	github.com/pelletier/go-toml/v2 v2.2.4 // indirect | ||||
| 	github.com/pkg/errors v0.9.1 // indirect | ||||
| 	github.com/quic-go/qpack v0.5.1 // indirect | ||||
| 	github.com/quic-go/quic-go v0.54.0 // indirect | ||||
| 	github.com/sagikazarmark/locafero v0.11.0 // indirect | ||||
| 	github.com/sirupsen/logrus v1.9.3 // indirect | ||||
| 	github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect | ||||
| 	github.com/spf13/afero v1.15.0 // indirect | ||||
| 	github.com/spf13/cast v1.10.0 // indirect | ||||
| 	github.com/spf13/cobra v1.10.1 // indirect | ||||
| 	github.com/spf13/pflag v1.0.10 // indirect | ||||
| 	github.com/spf13/viper v1.21.0 // indirect | ||||
| 	github.com/subosito/gotenv v1.6.0 // indirect | ||||
| 	github.com/twitchyliquid64/golang-asm v0.15.1 // indirect | ||||
| 	github.com/ugorji/go/codec v1.3.0 // indirect | ||||
| 	go.mills.io/bitcask/v2 v2.1.3 // indirect | ||||
| 	go.uber.org/mock v0.5.0 // indirect | ||||
| 	go.yaml.in/yaml/v3 v3.0.4 // indirect | ||||
| 	golang.org/x/arch v0.20.0 // indirect | ||||
| 	golang.org/x/crypto v0.43.0 // indirect | ||||
| 	golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect | ||||
| 	golang.org/x/mod v0.28.0 // indirect | ||||
| 	golang.org/x/net v0.45.0 // indirect | ||||
| 	golang.org/x/sync v0.17.0 // indirect | ||||
| 	golang.org/x/sys v0.37.0 // indirect | ||||
| 	golang.org/x/text v0.30.0 // indirect | ||||
| 	golang.org/x/tools v0.37.0 // indirect | ||||
| 	google.golang.org/protobuf v1.36.9 // indirect | ||||
| ) | ||||
							
								
								
									
										166
									
								
								go.sum
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										166
									
								
								go.sum
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,166 @@ | |||
| github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8= | ||||
| github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= | ||||
| github.com/abcum/lcp v0.0.0-20201209214815-7a3f3840be81 h1:uHogIJ9bXH75ZYrXnVShHIyywFiUZ7OOabwd9Sfd8rw= | ||||
| github.com/abcum/lcp v0.0.0-20201209214815-7a3f3840be81/go.mod h1:6ZvnjTZX1LNo1oLpfaJK8h+MXqHxcBFBIwkgsv+xlv0= | ||||
| github.com/bytedance/sonic v1.14.0 h1:/OfKt8HFw0kh2rj8N0F6C/qPGRESq0BbaNZgcNXXzQQ= | ||||
| github.com/bytedance/sonic v1.14.0/go.mod h1:WoEbx8WTcFJfzCe0hbmyTGrfjt8PzNEBdxlNUO24NhA= | ||||
| github.com/bytedance/sonic/loader v0.3.0 h1:dskwH8edlzNMctoruo8FPTJDF3vLtDT0sXZwvZJyqeA= | ||||
| github.com/bytedance/sonic/loader v0.3.0/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI= | ||||
| github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M= | ||||
| github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU= | ||||
| github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= | ||||
| github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||||
| github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||||
| github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= | ||||
| github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= | ||||
| github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM= | ||||
| github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8= | ||||
| github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w= | ||||
| github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM= | ||||
| github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk= | ||||
| github.com/gin-gonic/gin v1.11.0/go.mod h1:+iq/FyxlGzII0KHiBGjuNn4UNENUlKbGlNmc+W50Dls= | ||||
| github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 h1:BP4M0CvQ4S3TGls2FvczZtj5Re/2ZzkV9VwqPHH/3Bo= | ||||
| github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= | ||||
| github.com/go-ldap/ldap v3.4.12+incompatible h1:rxzUa+YRcrq19g6GYBUHsYVKSVIiY2uvprRFCdR/t5A= | ||||
| github.com/go-ldap/ldap v3.4.12+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc= | ||||
| github.com/go-ldap/ldap/v3 v3.4.12 h1:1b81mv7MagXZ7+1r7cLTWmyuTqVqdwbtJSjC0DAp9s4= | ||||
| github.com/go-ldap/ldap/v3 v3.4.12/go.mod h1:+SPAGcTtOfmGsCb3h1RFiq4xpp4N636G75OEace8lNo= | ||||
| github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= | ||||
| github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= | ||||
| github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= | ||||
| github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= | ||||
| github.com/go-playground/validator/v10 v10.27.0 h1:w8+XrWVMhGkxOaaowyKH35gFydVHOvC0/uWoy2Fzwn4= | ||||
| github.com/go-playground/validator/v10 v10.27.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo= | ||||
| github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= | ||||
| github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= | ||||
| github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= | ||||
| github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= | ||||
| github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw= | ||||
| github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= | ||||
| github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= | ||||
| github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= | ||||
| github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= | ||||
| github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= | ||||
| github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= | ||||
| github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= | ||||
| github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= | ||||
| github.com/hashicorp/go-immutable-radix/v2 v2.0.0 h1:nq9lQ5I71Heg2lRb2/+szuIWKY3Y73d8YKyXyN91WzU= | ||||
| github.com/hashicorp/go-immutable-radix/v2 v2.0.0/go.mod h1:hgdqLXA4f6NIjRVisM1TJ9aOJVNRqKZj+xDGF6m7PBw= | ||||
| github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= | ||||
| github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= | ||||
| github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= | ||||
| github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= | ||||
| github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= | ||||
| github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= | ||||
| github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= | ||||
| github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= | ||||
| github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= | ||||
| github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= | ||||
| github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= | ||||
| github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= | ||||
| github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= | ||||
| github.com/mattetti/filebuffer v1.0.1 h1:gG7pyfnSIZCxdoKq+cPa8T0hhYtD9NxCdI4D7PTjRLM= | ||||
| github.com/mattetti/filebuffer v1.0.1/go.mod h1:YdMURNDOttIiruleeVr6f56OrMc+MydEnTcXwtkxNVs= | ||||
| github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= | ||||
| github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= | ||||
| github.com/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuErjs= | ||||
| github.com/mattn/go-sqlite3 v1.14.32/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= | ||||
| github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= | ||||
| github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= | ||||
| github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= | ||||
| github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= | ||||
| github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= | ||||
| github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= | ||||
| github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= | ||||
| github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= | ||||
| github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||||
| github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= | ||||
| github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= | ||||
| github.com/quic-go/quic-go v0.54.0 h1:6s1YB9QotYI6Ospeiguknbp2Znb/jZYjZLRXn9kMQBg= | ||||
| github.com/quic-go/quic-go v0.54.0/go.mod h1:e68ZEaCdyviluZmy44P6Iey98v/Wfz6HCjQEm+l8zTY= | ||||
| github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= | ||||
| github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc= | ||||
| github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik= | ||||
| github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= | ||||
| github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= | ||||
| github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= | ||||
| github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= | ||||
| github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= | ||||
| github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw= | ||||
| github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U= | ||||
| github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= | ||||
| github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= | ||||
| github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= | ||||
| github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= | ||||
| github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s= | ||||
| github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0= | ||||
| github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= | ||||
| github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= | ||||
| github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= | ||||
| github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU= | ||||
| github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY= | ||||
| github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | ||||
| github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= | ||||
| github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= | ||||
| github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= | ||||
| github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | ||||
| github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | ||||
| github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= | ||||
| github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= | ||||
| github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= | ||||
| github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= | ||||
| github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= | ||||
| github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= | ||||
| github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA= | ||||
| github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4= | ||||
| go.mills.io/bitcask/v2 v2.1.3 h1:xOe0sdkTHqgpMxsfDjcpaWOm8VtCmA5JzRy4dbicFfk= | ||||
| go.mills.io/bitcask/v2 v2.1.3/go.mod h1:ZQFykoTTCvMwy24lBstZhSRQuleYIB4EzWKSOgEv6+k= | ||||
| go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= | ||||
| go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= | ||||
| go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= | ||||
| go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= | ||||
| golang.org/x/arch v0.20.0 h1:dx1zTU0MAE98U+TQ8BLl7XsJbgze2WnNKF/8tGp/Q6c= | ||||
| golang.org/x/arch v0.20.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk= | ||||
| golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= | ||||
| golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= | ||||
| golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY= | ||||
| golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= | ||||
| golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0= | ||||
| golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= | ||||
| golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= | ||||
| golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w= | ||||
| golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= | ||||
| golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ= | ||||
| golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI= | ||||
| golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= | ||||
| golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs= | ||||
| golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= | ||||
| golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM= | ||||
| golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= | ||||
| golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= | ||||
| golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= | ||||
| golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= | ||||
| golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||||
| golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= | ||||
| golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= | ||||
| golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= | ||||
| golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= | ||||
| golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | ||||
| golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4= | ||||
| golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= | ||||
| golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= | ||||
| golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= | ||||
| golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= | ||||
| golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= | ||||
| golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= | ||||
| golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo= | ||||
| golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg= | ||||
| golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw= | ||||
| golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w= | ||||
| google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw= | ||||
| google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= | ||||
| gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||||
| gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | ||||
| gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | ||||
							
								
								
									
										8
									
								
								internal/accounting/common.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								internal/accounting/common.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,8 @@ | |||
| package accounting | ||||
| 
 | ||||
| type Account interface { | ||||
| 	IsUser(name string) bool | ||||
| 	IsGroup(name string) bool | ||||
| 	UserInGroup(user string, group string) bool | ||||
| 	AuthUser(name string, password string) bool | ||||
| } | ||||
							
								
								
									
										117
									
								
								internal/accounting/ldap_fed.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								internal/accounting/ldap_fed.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,117 @@ | |||
| package accounting | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"log" | ||||
| 
 | ||||
| 	"github.com/go-ldap/ldap/v3" | ||||
| ) | ||||
| 
 | ||||
| type LDAP_Config struct { | ||||
| 	Base_DN string `mapstructure:"base_dn"` | ||||
| 
 | ||||
| 	User_iden     string `mapstructure:"user_identify"` | ||||
| 	User_objclass string `mapstructure:"user_objectclass"` | ||||
| 
 | ||||
| 	Group_iden     string `mapstructure:"group_identify"` | ||||
| 	Group_base     string `mapstructure:"group_base_dn"` | ||||
| 	Group_objclass string `mapstructure:"group_objectclass"` | ||||
| 
 | ||||
| 	Auth_username string `mapstructure:"auth_user"` | ||||
| 	Auth_password string `mapstructure:"auth_password"` | ||||
| 	Base_URL      string `mapstructure:"base_url"` | ||||
| } | ||||
| type LDAP struct { | ||||
| 	conn *ldap.Conn | ||||
| 	cfg  LDAP_Config | ||||
| } | ||||
| 
 | ||||
| func (l *LDAP) build_query(filter string) *ldap.SearchRequest { | ||||
| 	return ldap.NewSearchRequest(l.cfg.Base_DN, | ||||
| 		ldap.ScopeSingleLevel, ldap.NeverDerefAliases, 0, 0, false, | ||||
| 		filter, | ||||
| 		[]string{"dn"}, nil) | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| func (l *LDAP) IsUser(name string) bool { | ||||
| 	req := l.build_query( | ||||
| 		fmt.Sprintf("(&(objectClass=%s)(%s=%s))", | ||||
| 			l.cfg.User_objclass, l.cfg.User_iden, ldap.EscapeFilter(name), | ||||
| 		), | ||||
| 	) | ||||
| 	sr, err := l.conn.Search(req) | ||||
| 	if err != nil { | ||||
| 		log.Println(err) | ||||
| 		return false | ||||
| 	} | ||||
| 	if len(sr.Entries) != 1 { | ||||
| 		log.Println("Found multiple entries for single user") | ||||
| 		return false | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| func (l *LDAP) IsGroup(name string) bool { | ||||
| 	log.Printf("LDAP: Checking if group %s exists...\n", name) | ||||
| 	req := l.build_query( | ||||
| 		fmt.Sprintf("(&(objectClass=%s)(%s=%s))", | ||||
| 			l.cfg.Group_objclass, l.cfg.Group_iden, ldap.EscapeFilter(name), | ||||
| 		), | ||||
| 	) | ||||
| 	sr, err := l.conn.Search(req) | ||||
| 	if err != nil { | ||||
| 		log.Println(err) | ||||
| 		return false | ||||
| 	} | ||||
| 	if len(sr.Entries) != 1 { | ||||
| 		log.Println("Found multiple entries for single group") | ||||
| 		return false | ||||
| 	} | ||||
| 
 | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| func (l *LDAP) UserInGroup(user string, group string) bool { | ||||
| 	log.Printf("LDAP: Checking if user %s is in group %s...\n", user, group) | ||||
| 
 | ||||
| 	req := l.build_query( | ||||
| 		fmt.Sprintf("(&(objectClass=%s)(%s=%s)(memberof=%s=%s,%s))", | ||||
| 			l.cfg.User_objclass, | ||||
| 			l.cfg.User_iden, | ||||
| 			ldap.EscapeFilter(user), | ||||
| 			l.cfg.Group_iden, | ||||
| 			ldap.EscapeFilter(group), | ||||
| 			l.cfg.Group_base, | ||||
| 		), | ||||
| 	) | ||||
| 	sr, err := l.conn.Search(req) | ||||
| 	if err != nil { | ||||
| 		log.Println(err) | ||||
| 		return false | ||||
| 	} | ||||
| 
 | ||||
| 	return len(sr.Entries) != 1 | ||||
| } | ||||
| 
 | ||||
| func (l *LDAP) AuthUser(name string, password string) bool { | ||||
| 	log.Printf("LDAP: Attempting authentication for user %s...\n", name) | ||||
| 
 | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| func NewLDAP(config LDAP_Config) LDAP { | ||||
| 	new_dial, err := ldap.DialURL(config.Base_URL) | ||||
| 	if err != nil { | ||||
| 		log.Fatal(err) | ||||
| 	} | ||||
| 	err = new_dial.Bind(config.Auth_username, config.Auth_password) | ||||
| 	if err != nil { | ||||
| 		log.Fatal(err) | ||||
| 	} | ||||
| 
 | ||||
| 	return LDAP{ | ||||
| 		conn: new_dial, | ||||
| 		cfg:  config, | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										49
									
								
								internal/cli/federation.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								internal/cli/federation.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,49 @@ | |||
| package cli | ||||
| 
 | ||||
| import ( | ||||
| 	"log" | ||||
| 
 | ||||
| 	"github.com/jasinco/crtman/internal/accounting" | ||||
| 	"github.com/spf13/viper" | ||||
| ) | ||||
| 
 | ||||
| var backend accounting.Account | ||||
| var auth_backend map[string]*accounting.Account | ||||
| 
 | ||||
| func ParseFederation() { | ||||
| 	log.Println("Getting Federation") | ||||
| 	account_backend := viper.GetString("federation.backend") | ||||
| 	log.Printf("Accounting: %s\n", account_backend) | ||||
| 	auth := viper.GetStringSlice("federation.auth") | ||||
| 	log.Println("Authenticate", auth) | ||||
| 
 | ||||
| 	backend = construct(account_backend) | ||||
| 
 | ||||
| 	auth_backend = make(map[string]*accounting.Account) | ||||
| 	for _, method := range auth { | ||||
| 		if method == account_backend { | ||||
| 			auth_backend[method] = &backend | ||||
| 		} else { | ||||
| 			auth := construct(method) | ||||
| 			auth_backend[method] = &auth | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| func construct_ldap() accounting.LDAP { | ||||
| 	block_name := "federation.ldap" | ||||
| 	var ldap_cfg accounting.LDAP_Config | ||||
| 	viper.UnmarshalKey(block_name, &ldap_cfg) | ||||
| 	return accounting.NewLDAP(ldap_cfg) | ||||
| } | ||||
| 
 | ||||
| func construct(name string) accounting.Account { | ||||
| 	var account accounting.Account | ||||
| 	switch name { | ||||
| 	case "ldap": | ||||
| 		ldap_inst := construct_ldap() | ||||
| 		account = &ldap_inst | ||||
| 	default: | ||||
| 		log.Fatal("No Federation") | ||||
| 	} | ||||
| 	return account | ||||
| } | ||||
							
								
								
									
										44
									
								
								internal/cli/helper.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								internal/cli/helper.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,44 @@ | |||
| package cli | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 
 | ||||
| 	"github.com/spf13/viper" | ||||
| ) | ||||
| 
 | ||||
| type CA_CFG struct { | ||||
| 	Org  string | ||||
| 	FQDN string | ||||
| } | ||||
| 
 | ||||
| var Host string | ||||
| var Port string | ||||
| var PersistentPath string | ||||
| var CA_config CA_CFG | ||||
| 
 | ||||
| // the real endpoint that you should access the server | ||||
| // e.g. 172.16.0.1:3232 | ||||
| var Outbound string | ||||
| 
 | ||||
| func GetListen() { | ||||
| 	viper.SetDefault("host", "localhost") | ||||
| 	viper.SetDefault("port", "8086") | ||||
| 	Host = viper.GetString("host") | ||||
| 	Port = viper.GetString("port") | ||||
| 	viper.SetDefault("outbound", fmt.Sprintf("%s:%s", Host, Port)) | ||||
| 	Outbound = viper.GetString("outbound") | ||||
| } | ||||
| 
 | ||||
| func GetPersistent() { | ||||
| 	viper.SetDefault("store", "/tmp/db") | ||||
| 	PersistentPath = viper.GetString("store") | ||||
| } | ||||
| 
 | ||||
| func GetCA_CFG() { | ||||
| 	viper.SetDefault("ca.organization", "TCIVS_CSE_CR") | ||||
| 	viper.SetDefault("ca.fqdn", "example.com") | ||||
| 	CA_config = CA_CFG{ | ||||
| 		Org:  viper.GetString("ca.organization"), | ||||
| 		FQDN: viper.GetString("ca.fqdn"), | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										40
									
								
								internal/crt/cert.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								internal/crt/cert.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,40 @@ | |||
| package crt | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"database/sql" | ||||
| 
 | ||||
| 	"github.com/jasinco/crtman/internal/store" | ||||
| 	"golang.org/x/crypto/ocsp" | ||||
| ) | ||||
| 
 | ||||
| func CheckValid(req *ocsp.Request) sql.Null[ocsp.Response] { | ||||
| 	var response ocsp.Response | ||||
| 	response.Status = ocsp.Revoked | ||||
| 	if !req.HashAlgorithm.Available() { | ||||
| 		return sql.Null[ocsp.Response]{Valid: false} | ||||
| 	} | ||||
| 
 | ||||
| 	leaf := store.GetLeafCert(req.SerialNumber) | ||||
| 	if !leaf.Valid { | ||||
| 		return sql.Null[ocsp.Response]{Valid: false} | ||||
| 	} | ||||
| 
 | ||||
| 	hasher := req.HashAlgorithm.New() | ||||
| 	valid_issuer_dn := bytes.Equal(hasher.Sum(leaf.V.Cert.RawIssuer), req.IssuerKeyHash) | ||||
| 	hasher.Reset() | ||||
| 	valid_issuer_key := bytes.Equal(hasher.Sum(leaf.V.Cert.RawSubjectPublicKeyInfo), req.IssuerKeyHash) | ||||
| 
 | ||||
| 	if !(valid_issuer_dn && valid_issuer_key) { | ||||
| 		response.Status = ocsp.Unknown | ||||
| 	} | ||||
| 	response.SerialNumber = req.SerialNumber | ||||
| 
 | ||||
| 	if !leaf.V.RevokeAt.Valid { | ||||
| 		response.Status = ocsp.Good | ||||
| 	} else { | ||||
| 		response.RevokedAt = leaf.V.RevokeAt.Time | ||||
| 		response.RevocationReason = int(leaf.V.RevokeReason.Int16) | ||||
| 	} | ||||
| 	return sql.Null[ocsp.Response]{Valid: true, V: response} | ||||
| } | ||||
							
								
								
									
										92
									
								
								internal/crt/issue.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								internal/crt/issue.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,92 @@ | |||
| package crt | ||||
| 
 | ||||
| import ( | ||||
| 	"crypto/ecdsa" | ||||
| 	"crypto/elliptic" | ||||
| 	"crypto/rand" | ||||
| 	"crypto/x509" | ||||
| 	"crypto/x509/pkix" | ||||
| 	"database/sql" | ||||
| 	"fmt" | ||||
| 	"log" | ||||
| 	"math/big" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/jasinco/crtman/internal/cli" | ||||
| 	"github.com/jasinco/crtman/internal/store" | ||||
| ) | ||||
| 
 | ||||
| func IssueRoot(config cli.CA_CFG) { | ||||
| 	log.Printf("CA: Org: %s, FQDN: %s\n", config.Org, config.FQDN) | ||||
| 	rootca := x509.Certificate{ | ||||
| 		IsCA:        true, | ||||
| 		Version:     1, | ||||
| 		KeyUsage:    x509.KeyUsageCertSign | x509.KeyUsageCRLSign | x509.KeyUsageDigitalSignature, | ||||
| 		ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageOCSPSigning}, | ||||
| 		Subject: pkix.Name{ | ||||
| 			Country:      []string{"Taiwan"}, | ||||
| 			Organization: []string{config.Org}, | ||||
| 			CommonName:   config.FQDN, | ||||
| 		}, | ||||
| 		NotBefore:             time.Now(), | ||||
| 		BasicConstraintsValid: true, | ||||
| 		SerialNumber:          big.NewInt(0), | ||||
| 	} | ||||
| 	privkey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) | ||||
| 	if err != nil { | ||||
| 		log.Fatal(err) | ||||
| 	} | ||||
| 
 | ||||
| 	crt, err := x509.CreateCertificate(rand.Reader, &rootca, &rootca, &privkey.PublicKey, privkey) | ||||
| 	if err != nil { | ||||
| 		log.Fatal("can't create x509 ca, ", err) | ||||
| 	} | ||||
| 	// privkey_bytes, err := x509.MarshalECPrivateKey(privkey) | ||||
| 	// if err != nil { | ||||
| 	// 	log.Fatal("can't create ecdsa key encoded, ", err) | ||||
| 	// } | ||||
| 	store.RootCA = crt | ||||
| 	store.RootCAKey = privkey | ||||
| } | ||||
| 
 | ||||
| // csr is a DER csr | ||||
| func IssueCert(csr []byte) sql.Null[[]byte] { | ||||
| 	req, err := x509.ParseCertificateRequest(csr) | ||||
| 	if err != nil { | ||||
| 		return sql.Null[[]byte]{Valid: false} | ||||
| 	} | ||||
| 	if req.CheckSignature() != nil { | ||||
| 		return sql.Null[[]byte]{Valid: false} | ||||
| 	} | ||||
| 
 | ||||
| 	store.SerialLock.Lock() | ||||
| 	defer store.SerialLock.Unlock() | ||||
| 	cert := x509.Certificate{ | ||||
| 		Subject:               req.Subject, | ||||
| 		PublicKey:             req.PublicKey, | ||||
| 		PublicKeyAlgorithm:    req.PublicKeyAlgorithm, | ||||
| 		NotBefore:             time.Now(), | ||||
| 		NotAfter:              time.Now().Add(time.Hour * 24 * 720), | ||||
| 		KeyUsage:              x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment, | ||||
| 		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, | ||||
| 		IsCA:                  false, | ||||
| 		BasicConstraintsValid: true, | ||||
| 		DNSNames:              req.DNSNames, | ||||
| 		IPAddresses:           req.IPAddresses, | ||||
| 		SerialNumber:          &store.Serial, | ||||
| 		OCSPServer:            []string{fmt.Sprintf("https://%s/api/ocsp", cli.Outbound)}, | ||||
| 	} | ||||
| 	ca, err := x509.ParseCertificate(store.RootCA) | ||||
| 	if err != nil { | ||||
| 		log.Println(err) | ||||
| 		return sql.Null[[]byte]{Valid: false} | ||||
| 	} | ||||
| 	signed, err := x509.CreateCertificate(rand.Reader, &cert, ca, req.PublicKey, store.RootCAKey) | ||||
| 	if err != nil { | ||||
| 		log.Println(err) | ||||
| 		return sql.Null[[]byte]{Valid: false} | ||||
| 	} | ||||
| 	store.Serial.Add(&store.Serial, big.NewInt(1)) | ||||
| 
 | ||||
| 	return sql.Null[[]byte]{Valid: true, V: signed} | ||||
| } | ||||
							
								
								
									
										123
									
								
								internal/store/storage.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										123
									
								
								internal/store/storage.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,123 @@ | |||
| package store | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"crypto/ecdsa" | ||||
| 	"crypto/x509" | ||||
| 	"database/sql" | ||||
| 	"log" | ||||
| 	"math/big" | ||||
| 	"sync" | ||||
| 
 | ||||
| 	_ "github.com/mattn/go-sqlite3" | ||||
| ) | ||||
| 
 | ||||
| var DB *sql.DB | ||||
| var RootCA []byte | ||||
| var RootCAKey *ecdsa.PrivateKey | ||||
| var SerialLock sync.Mutex | ||||
| var Serial big.Int | ||||
| 
 | ||||
| func InitStore(path string) { | ||||
| 	var err error | ||||
| 	DB, err = sql.Open("sqlite3", path) | ||||
| 	if err != nil { | ||||
| 		log.Fatal(err) | ||||
| 	} | ||||
| } | ||||
| func CloseStore() { | ||||
| 	DB.Close() | ||||
| } | ||||
| func ChekInit() bool { | ||||
| 	result := DB.QueryRow("select id,cert,key,serial from syscfg order by id desc limit 1") | ||||
| 	if result.Err() != nil { | ||||
| 		log.Println(result.Err()) | ||||
| 		return false | ||||
| 	} | ||||
| 	var placeholder int | ||||
| 	var temp_serial, temp_ca_key []byte | ||||
| 	err := result.Scan(&placeholder, &RootCA, &temp_ca_key, &temp_serial) | ||||
| 	if err != nil { | ||||
| 		log.Println(err) | ||||
| 		return false | ||||
| 	} | ||||
| 	Serial.SetBytes(temp_serial) | ||||
| 	RootCAKey, err = x509.ParseECPrivateKey(temp_ca_key) | ||||
| 	if err != nil { | ||||
| 		log.Println(err) | ||||
| 		return false | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
| func initSchema(tx *sql.Tx) { | ||||
| 	_, err := tx.Exec(` | ||||
| 	create table syscfg (id integer not null primary key autoincrement, cert blob not null, key blob not null, serial blob not null); | ||||
| 	delete from syscfg; | ||||
| 	`) | ||||
| 	if err != nil { | ||||
| 		log.Fatal(err) | ||||
| 	} | ||||
| 	_, err = tx.Exec(` | ||||
| 	create table leaf (id blob not null primary key, cert blob not null, owner text not null, revokeAt datetime, revokeReason integer); | ||||
| 	delete from leaf; | ||||
| 	`) | ||||
| } | ||||
| func InitStoreData() { | ||||
| 	ctx := context.Background() | ||||
| 	tx, err := DB.BeginTx(ctx, nil) | ||||
| 	if err != nil { | ||||
| 		log.Fatal(err) | ||||
| 	} | ||||
| 	initSchema(tx) | ||||
| 
 | ||||
| 	temp_ca_key, err := x509.MarshalECPrivateKey(RootCAKey) | ||||
| 	if err != nil { | ||||
| 		log.Fatal(err) | ||||
| 	} | ||||
| 
 | ||||
| 	tx.Exec(` | ||||
| 	insert into syscfg(cert,key,serial) values(?,?,?) | ||||
| 	`, RootCA, temp_ca_key, Serial.Bytes()) | ||||
| 	err = tx.Commit() | ||||
| 	if err != nil { | ||||
| 		log.Fatal(err) | ||||
| 	} | ||||
| } | ||||
| func InsertCert(cert []byte, applicant string) { | ||||
| 	parse, err := x509.ParseCertificate(cert) | ||||
| 	if err != nil { | ||||
| 		log.Println(err) | ||||
| 		return | ||||
| 	} | ||||
| 	DB.Exec("insert into syscfg(id,cert,owner) values(?,?,?)", parse.SerialNumber.Bytes(), cert, applicant) | ||||
| } | ||||
| 
 | ||||
| type LeafCert struct { | ||||
| 	Cert_org     []byte | ||||
| 	Cert         *x509.Certificate | ||||
| 	RevokeAt     sql.NullTime | ||||
| 	RevokeReason sql.NullInt16 | ||||
| } | ||||
| 
 | ||||
| func GetLeafCert(serial_num *big.Int) sql.Null[LeafCert] { | ||||
| 	var err error | ||||
| 	result := DB.QueryRow("select cert,revokeAt, revokeReason from leaf where id = ?", serial_num.Bytes()) | ||||
| 	var leaf LeafCert | ||||
| 	if result.Err() != nil { | ||||
| 		log.Println(result.Err()) | ||||
| 		return sql.Null[LeafCert]{Valid: false} | ||||
| 	} | ||||
| 
 | ||||
| 	err = result.Scan(&leaf.Cert_org, &leaf.RevokeAt, &leaf.RevokeReason) | ||||
| 	if err != nil { | ||||
| 		log.Println(err) | ||||
| 		return sql.Null[LeafCert]{Valid: false} | ||||
| 	} | ||||
| 	leaf.Cert, err = x509.ParseCertificate(leaf.Cert_org) | ||||
| 	if err != nil { | ||||
| 		log.Println(err) | ||||
| 		return sql.Null[LeafCert]{Valid: false} | ||||
| 	} | ||||
| 	return sql.Null[LeafCert]{V: leaf, Valid: true} | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										56
									
								
								internal/web/ocsp.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								internal/web/ocsp.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,56 @@ | |||
| package web | ||||
| 
 | ||||
| import ( | ||||
| 	"crypto/x509" | ||||
| 	"encoding/base64" | ||||
| 	"io" | ||||
| 	"log" | ||||
| 
 | ||||
| 	"github.com/gin-gonic/gin" | ||||
| 	"github.com/jasinco/crtman/internal/crt" | ||||
| 	"github.com/jasinco/crtman/internal/store" | ||||
| 	"golang.org/x/crypto/ocsp" | ||||
| ) | ||||
| 
 | ||||
| // comply to https://datatracker.ietf.org/doc/html/rfc6960#appendix-A | ||||
| func ocsp_handling(c *gin.Context) { | ||||
| 	var req_bin []byte | ||||
| 	var err error | ||||
| 	if c.Request.Method == "GET" { | ||||
| 		b64_req := c.Param("req") | ||||
| 		req_bin, err = base64.RawURLEncoding.DecodeString(b64_req) | ||||
| 		if err != nil { | ||||
| 			c.Status(400) | ||||
| 			return | ||||
| 		} | ||||
| 	} else { | ||||
| 		req_bin, err = io.ReadAll(c.Request.Body) | ||||
| 		if err != nil { | ||||
| 			c.Status(400) | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
| 	req, err := ocsp.ParseRequest(req_bin) | ||||
| 	if err != nil { | ||||
| 		c.Status(400) | ||||
| 		return | ||||
| 	} | ||||
| 	result := crt.CheckValid(req) | ||||
| 	if !result.Valid { | ||||
| 		c.Status(400) | ||||
| 		return | ||||
| 	} | ||||
| 	ca, err := x509.ParseCertificate(store.RootCA) | ||||
| 	if err != nil { | ||||
| 		log.Println(err) | ||||
| 		c.Status(500) | ||||
| 		return | ||||
| 	} | ||||
| 	response, err := ocsp.CreateResponse(ca, ca, result.V, store.RootCAKey) | ||||
| 	if err != nil { | ||||
| 		log.Println(err) | ||||
| 		c.Status(500) | ||||
| 		return | ||||
| 	} | ||||
| 	c.Data(200, "application/ocsp-response", response) | ||||
| } | ||||
							
								
								
									
										29
									
								
								internal/web/serve.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								internal/web/serve.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,29 @@ | |||
| package web | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/pem" | ||||
| 	"fmt" | ||||
| 	"log" | ||||
| 
 | ||||
| 	"github.com/gin-gonic/gin" | ||||
| 	"github.com/jasinco/crtman/internal/store" | ||||
| ) | ||||
| 
 | ||||
| func Listen(host string, port string) { | ||||
| 	r := gin.Default() | ||||
| 	r.GET("/api/rootca.pem", func(ctx *gin.Context) { | ||||
| 		ca_pem := pem.Block{ | ||||
| 			Type:  "CERTIFICATE", | ||||
| 			Bytes: store.RootCA, | ||||
| 		} | ||||
| 
 | ||||
| 		ctx.Data(200, "application/x-x509-ca-cert", pem.EncodeToMemory(&ca_pem)) | ||||
| 	}) | ||||
| 	r.GET("/api/ocsp/:req", ocsp_handling) | ||||
| 	r.POST("/api/ocsp", ocsp_handling) | ||||
| 
 | ||||
| 	err := r.Run(fmt.Sprintf("%s:%s", host, port)) | ||||
| 	if err != nil { | ||||
| 		log.Fatal(err) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										4
									
								
								justfile
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								justfile
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,4 @@ | |||
| run: | ||||
|     CGO_ENABLED=1 go run ./main.go serve | ||||
| serveinit: | ||||
|     CGO_ENABLED=1 go run ./main.go serve --init | ||||
							
								
								
									
										21
									
								
								main.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								main.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,21 @@ | |||
| /* | ||||
| Copyright © 2025 NAME HERE <EMAIL ADDRESS> | ||||
| */ | ||||
| package main | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/jasinco/crtman/cmd" | ||||
| 	"github.com/spf13/viper" | ||||
| ) | ||||
| 
 | ||||
| func init() { | ||||
| 	viper.SetConfigName("crtman.toml") | ||||
| 	viper.AddConfigPath("/etc/") | ||||
| 	viper.AddConfigPath("$HOME/") | ||||
| 	viper.AddConfigPath(".") | ||||
| 	viper.SetConfigType("toml") | ||||
| } | ||||
| 
 | ||||
| func main() { | ||||
| 	cmd.Execute() | ||||
| } | ||||
		Loading…
	
	Add table
		
		Reference in a new issue