diff --git a/cmd/cgr-engine/cgr-engine.go b/cmd/cgr-engine/cgr-engine.go index 7f8df125b..32253a4b3 100644 --- a/cmd/cgr-engine/cgr-engine.go +++ b/cmd/cgr-engine/cgr-engine.go @@ -530,7 +530,12 @@ func startRpc(server *utils.Server, internalRaterChan, } go server.ServeJSON(cfg.RPCJSONListen) go server.ServeGOB(cfg.RPCGOBListen) - go server.ServeHTTP(cfg.HTTPListen) + go server.ServeHTTP( + cfg.HTTPListen, + cfg.HTTPApiUseBasicAuth, + cfg.HTTPApiBasicAuthRealm, + cfg.HTTPApiHtpasswdFile, + ) } func writePid() { diff --git a/config/config.go b/config/config.go index a7acb3ed2..3b255e9c4 100644 --- a/config/config.go +++ b/config/config.go @@ -203,6 +203,9 @@ type CGRConfig struct { RPCJSONListen string // RPC JSON listening address RPCGOBListen string // RPC GOB listening address HTTPListen string // HTTP listening address + HTTPApiUseBasicAuth bool // Use basic auth for HTTP API + HTTPApiBasicAuthRealm string // Basic auth realm URL + HTTPApiHtpasswdFile string // Basic auth htpasswd file path DefaultReqType string // Use this request type if not defined on top DefaultCategory string // set default type of record DefaultTenant string // set default tenant @@ -503,6 +506,11 @@ func (self *CGRConfig) loadFromJsonCfg(jsnCfg *CgrJsonCfg) error { return err } + jsnHttpApiCfg, err := jsnCfg.HttpApiJsonCfg() + if err != nil { + return err + } + jsnTpDbCfg, err := jsnCfg.DbJsonCfg(TPDB_JSN) if err != nil { return err @@ -779,6 +787,18 @@ func (self *CGRConfig) loadFromJsonCfg(jsnCfg *CgrJsonCfg) error { } } + if jsnHttpApiCfg != nil { + if jsnHttpApiCfg.Use_basic_auth != nil { + self.HTTPApiUseBasicAuth = *jsnHttpApiCfg.Use_basic_auth + } + if jsnHttpApiCfg.Basic_auth_realm != nil { + self.HTTPApiBasicAuthRealm = *jsnHttpApiCfg.Basic_auth_realm + } + if jsnHttpApiCfg.Htpasswd_file != nil { + self.HTTPApiHtpasswdFile = *jsnHttpApiCfg.Htpasswd_file + } + } + if jsnRALsCfg != nil { if jsnRALsCfg.Enabled != nil { self.RALsEnabled = *jsnRALsCfg.Enabled diff --git a/config/config_defaults.go b/config/config_defaults.go index eec70217b..b65eeaa6b 100644 --- a/config/config_defaults.go +++ b/config/config_defaults.go @@ -45,7 +45,7 @@ const CGRATES_CFG_JSON = ` "internal_ttl": "2m", // maximum duration to wait for internal connections before giving up "locking_timeout": "5s", // timeout internal locks to avoid deadlocks "cache_dump_dir": "", // cache dump for faster start (leave empty to disable) - "log_level": 6, // control the level of messages logged (0-emerg to 7-debug) + "log_level": 6, // control the level of messages logged (0-emerg to 7-debug) }, @@ -72,6 +72,13 @@ const CGRATES_CFG_JSON = ` }, +"http_api": { // HTTP API configuration + "use_basic_auth": false, // use basic authentication + "basic_auth_realm": "", // basic auth realm URL + "htpasswd_file": "" // basic auth htpasswd file location +}, + + "tariffplan_db": { // database used to store active tariff plan configuration "db_type": "redis", // tariffplan_db type: "db_host": "127.0.0.1", // tariffplan_db host address @@ -244,7 +251,7 @@ const CGRATES_CFG_JSON = ` "sms_usage_multiply_factor": 1, // multiply data usage before export (eg: convert from SMS unit to call duration in some billing systems) "mms_usage_multiply_factor": 1, // multiply data usage before export (eg: convert from MMS unit to call duration in some billing systems) "generic_usage_multiply_factor": 1, // multiply data usage before export (eg: convert from GENERIC unit to call duration in some billing systems) - "cost_multiply_factor": 1, // multiply cost before export, eg: add VAT + "cost_multiply_factor": 1, // multiply cost before export, eg: add VAT "export_directory": "/var/spool/cgrates/cdre", // path where the exported CDRs will be placed "header_fields": [], // template of the exported header fields "content_fields": [ // template of the exported content fields diff --git a/config/config_json.go b/config/config_json.go index 317d03584..6246bbf7e 100644 --- a/config/config_json.go +++ b/config/config_json.go @@ -29,6 +29,7 @@ const ( GENERAL_JSN = "general" CACHE_JSN = "cache" LISTEN_JSN = "listen" + HTTP_API_JSN = "http_api" TPDB_JSN = "tariffplan_db" DATADB_JSN = "data_db" STORDB_JSN = "stor_db" @@ -118,6 +119,18 @@ func (self CgrJsonCfg) ListenJsonCfg() (*ListenJsonCfg, error) { return cfg, nil } +func (self CgrJsonCfg) HttpApiJsonCfg() (*HTTPApiJsonCfg, error) { + rawCfg, hasKey := self[HTTP_API_JSN] + if !hasKey { + return nil, nil + } + cfg := new(HTTPApiJsonCfg) + if err := json.Unmarshal(*rawCfg, cfg); err != nil { + return nil, err + } + return cfg, nil +} + func (self CgrJsonCfg) DbJsonCfg(section string) (*DbJsonCfg, error) { rawCfg, hasKey := self[section] if !hasKey { diff --git a/config/libconfig_json.go b/config/libconfig_json.go index 6cbd8e7fc..45b92efec 100644 --- a/config/libconfig_json.go +++ b/config/libconfig_json.go @@ -46,6 +46,13 @@ type ListenJsonCfg struct { Http *string } +// HTTP API config section +type HTTPApiJsonCfg struct { + Use_basic_auth *bool + Basic_auth_realm *string + Htpasswd_file *string +} + // Database config type DbJsonCfg struct { Db_type *string diff --git a/data/conf/cgrates/cgrates.json b/data/conf/cgrates/cgrates.json index c54cf81ee..6eddf6e3b 100644 --- a/data/conf/cgrates/cgrates.json +++ b/data/conf/cgrates/cgrates.json @@ -51,6 +51,12 @@ // }, +// "http_api" { // HTTP API configuration +// "use_basic_auth": false, // use basic authentication +// "basic_auth_realm": "", // basic auth realm URL +// "htpasswd_file": "", // basic auth htpasswd file location +// }, + // "tariffplan_db": { // database used to store active tariff plan configuration // "db_type": "redis", // tariffplan_db type: // "db_host": "127.0.0.1", // tariffplan_db host address @@ -199,7 +205,7 @@ // "sms_usage_multiply_factor": 1, // multiply data usage before export (eg: convert from SMS unit to call duration in some billing systems) // "mms_usage_multiply_factor": 1, // multiply data usage before export (eg: convert from MMS unit to call duration in some billing systems) // "generic_usage_multiply_factor": 1, // multiply data usage before export (eg: convert from GENERIC unit to call duration in some billing systems) -// "cost_multiply_factor": 1, // multiply cost before export, eg: add VAT +// "cost_multiply_factor": 1, // multiply cost before export, eg: add VAT // "export_directory": "/var/spool/cgrates/cdre", // path where the exported CDRs will be placed // "header_fields": [], // template of the exported header fields // "content_fields": [ // template of the exported content fields diff --git a/glide.lock b/glide.lock index db06470c9..83f928653 100644 --- a/glide.lock +++ b/glide.lock @@ -1,6 +1,8 @@ -hash: da953ea34fabe4e21f4dde6344f3b7ab5a75e02122a1c17af5ea434058fc77fb -updated: 2016-09-06T20:33:01.649869367+02:00 +hash: cfda8a78a96bf7b3b471463d5ddf3330d6ae2089d5ac7c9a31b0f312e6518595 +updated: 2016-11-10T08:26:28.162167756-07:00 imports: +- name: github.com/abbot/go-http-auth + version: efc9484eee77263a11f158ef4f30fcc30298a942 - name: github.com/bit4bit/gami version: 3a7f98e7efce7ed7f22c2169b666910b8abb15dc - name: github.com/cenk/hub @@ -26,13 +28,17 @@ imports: - internal/parser/findutil - internal/parser/intfns - internal/parser/pathexpr - - internal/xconst - - internal/xsort + - literals/boollit + - literals/numlit + - literals/strlit - tree - tree/xmltree - tree/xmltree/xmlbuilder - tree/xmltree/xmlele - tree/xmltree/xmlnode + - xconst + - xfn + - xsort - name: github.com/DisposaBoy/JsonConfigReader version: 33a99fdf1d5ee1f79b5077e9c06f955ad356d5f4 - name: github.com/fiorix/go-diameter @@ -76,6 +82,11 @@ imports: version: 5cd0f2b3b6cca8e3a0a4101821e41a73cb59bed6 subpackages: - codec +- name: golang.org/x/crypto + version: 9477e0b78b9ac3d0b03822fd95422e2fe07627cd + subpackages: + - bcrypt + - blowfish - name: golang.org/x/net version: 1358eff22f0dd0c54fc521042cc607f6ff4b531a subpackages: diff --git a/glide.yaml b/glide.yaml index 0d927866f..6a48d4ba8 100644 --- a/glide.yaml +++ b/glide.yaml @@ -39,3 +39,5 @@ import: - package: github.com/hashicorp/golang-lru - package: github.com/cgrates/aringo - package: github.com/bit4bit/gami +- package: github.com/abbot/go-http-auth + version: ~0.3.0 diff --git a/utils/server.go b/utils/server.go index 50f2d5509..44f4b3631 100644 --- a/utils/server.go +++ b/utils/server.go @@ -29,8 +29,8 @@ import ( "reflect" "time" + "github.com/abbot/go-http-auth" "github.com/cenk/rpc2" - "golang.org/x/net/websocket" ) import _ "net/http/pprof" @@ -140,17 +140,23 @@ func (s *Server) ServeGOB(addr string) { } } -func (s *Server) ServeHTTP(addr string) { +func handleRequest(w http.ResponseWriter, r *http.Request) { + defer r.Body.Close() + w.Header().Set("Content-Type", "application/json") + res := NewRPCRequest(r.Body).Call() + io.Copy(w, res) +} + +func (s *Server) ServeHTTP(addr string, useBasicAuth bool, basicAuthRealm string, htpasswdFile string) { if s.rpcEnabled { - http.HandleFunc("/jsonrpc", func(w http.ResponseWriter, req *http.Request) { - defer req.Body.Close() - w.Header().Set("Content-Type", "application/json") - res := NewRPCRequest(req.Body).Call() - io.Copy(w, res) - }) - http.Handle("/ws", websocket.Handler(func(ws *websocket.Conn) { - jsonrpc.ServeConn(ws) - })) + if useBasicAuth { + Logger.Info(fmt.Sprintf("Configuring CGRateS HTTP server to use basic auth (realm: %s, htpasswd: %s).", basicAuthRealm, htpasswdFile)) + secrets := auth.HtpasswdFileProvider(htpasswdFile) + authenticator := auth.NewBasicAuthenticator(basicAuthRealm, secrets) + http.HandleFunc("/jsonrpc", auth.JustCheck(authenticator, handleRequest)) + } else { + http.HandleFunc("/jsonrpc", handleRequest) + } s.httpEnabled = true } if !s.httpEnabled {