/
config.go
193 lines (163 loc) · 5.57 KB
/
config.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
package config
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"regexp"
"strings"
"github.com/fabbricadigitale/scimd/validation"
homedir "github.com/mitchellh/go-homedir"
"github.com/spf13/viper"
validator "gopkg.in/go-playground/validator.v9"
"github.com/fabbricadigitale/scimd/defaults"
"github.com/fabbricadigitale/scimd/schemas/core"
"github.com/fatih/structs"
d "github.com/mcuadros/go-defaults"
)
// Configuration is ...
type Configuration struct {
Storage
Debug bool
Port int `default:"8787" validate:"min=1024,max=65535"`
ServiceProviderConfig string `validate:"omitempty,pathexists,isfile=.json"`
Config string `validate:"omitempty,pathexists,isdir,hassubdir=schemas,hassubdir=resources"` // (todo) > check the config directory contains two directories, one for resource types and one for schemas, and that them contains json files
PageSize int `default:"10" validate:"min=1,max=10"`
Enable
}
// Storage is ...
type Storage struct {
Type string `default:"mongo" validate:"eq=mongo"` // (note) > since we are only supporting mongo at the moment
Host string `default:"0.0.0.0" validate:"hostname|ip4_addr"`
Port int `default:"27017" validate:"min=1024,max=65535"`
Name string `default:"scimd" validate:"min=1,excludesall=/\\.*<>:?$\""` // cannot contain any of these characters /, \, ., *, <, >, :, , ?, $, " (fixme) exclude also => |
Coll string `default:"resources" validate:"min=1,excludes=$,nstartswith=system."`
}
// Enable is ...
type Enable struct {
Self bool
}
var (
// Values contains the configuration values
Values *Configuration
// Errors contains the happened configuration errors
Errors validator.ValidationErrors
)
var serviceProviderConfig core.ServiceProviderConfig
// getConfig is responsible to set configuration values
//
// The priority model from higher to lower is the following one.
// 0. Flags
// 1. Environment variables
// 2. Configuration file
func getConfig(filename string) {
Values = new(Configuration)
// Defaults
d.SetDefaults(Values)
for key, value := range structs.Map(Values) {
viper.SetDefault(key, value)
}
viper.SetConfigName(filename)
viper.AddConfigPath(".")
// Search home directory
home, err := homedir.Dir()
if err != nil {
panic(err)
}
viper.AddConfigPath(home)
viper.SetEnvPrefix("scimd")
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
viper.AutomaticEnv()
err = viper.ReadInConfig()
err = viper.Unmarshal(&Values)
// (todo) > better handling of errors - invalid syntax - etc. etc.
// exit(1) ?
if err != nil {
fmt.Println("xxxxxx")
panic(err)
}
// Validate the configurations and collect errors
_, err = Valid()
if err != nil {
errs, _ := err.(validator.ValidationErrors)
Errors = append(Errors, errs...)
}
}
func init() {
getConfig(".scimd")
// ServiceProviderConfig
serviceProviderConfig = defaults.ServiceProviderConfig
// Schemas
core.GetSchemaRepository().Push(defaults.UserSchema)
core.GetSchemaRepository().Push(defaults.GroupSchema)
// Resource types
core.GetResourceTypeRepository().Push(defaults.UserResourceType)
core.GetResourceTypeRepository().Push(defaults.GroupResourceType)
}
func getFilesWithExt(dir string, ext string) (files []string) {
re := regexp.MustCompile(`^\.?(.*)`)
filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
if !info.IsDir() {
if filepath.Ext(path) == re.ReplaceAllString(ext, `.$1`) {
files = append(files, path)
}
}
return nil
})
return
}
// Custom is responsible to load custom configurations
//
// First it checks whether user have specified custom configs.
// Then checks whether them are suitable and valid.
// Finally tries to load them.
func Custom() error {
// Check wheter user specified a custom service provider config
if Values.ServiceProviderConfig != "" {
dat, _ := ioutil.ReadFile(Values.ServiceProviderConfig)
serviceProviderConfig = *core.NewServiceProviderConfig()
if err := json.Unmarshal(dat, &serviceProviderConfig); err != nil {
if Values.Debug {
fmt.Fprintf(os.Stderr, err.Error())
}
return fmt.Errorf("Error unmarshalling custom service provider config (\"%s\")", Values.ServiceProviderConfig)
}
// Here default service provider config has been overridden
if errs := validation.Validator.Struct(serviceProviderConfig); errs != nil {
return fmt.Errorf(validation.Errors(errs))
}
}
// Check wheter user specified a custom location to provide its own resources (schemas + resource types)
if Values.Config != "" {
schemas := getFilesWithExt(filepath.Join(Values.Config, "schemas"), "json")
rstypes := getFilesWithExt(filepath.Join(Values.Config, "resources"), "json")
// (todo) > check correspondance between schemas and resource types or this is user's responsibility?
core.GetSchemaRepository().Clean()
for _, schema := range schemas {
_, err := core.GetSchemaRepository().PushFromFile(schema)
if err != nil {
return fmt.Errorf("Error loading schema (\"%s\")", schema)
}
}
core.GetResourceTypeRepository().Clean()
for _, rstype := range rstypes {
_, err := core.GetResourceTypeRepository().PushFromFile(rstype)
if err != nil {
return fmt.Errorf("Error loading resource type (\"%s\")", rstype)
}
}
}
return nil
}
// Valid checks wheter the configuration is valid or not
func Valid() (bool, error) {
if err := validation.Validator.Struct(Values); err != nil {
return false, err
}
return true, nil
}
// ServiceProviderConfig returns the current service provider config
func ServiceProviderConfig() core.ServiceProviderConfig {
return serviceProviderConfig
}