1
0
mirror of https://github.com/mainflux/mainflux.git synced 2025-04-29 13:49:28 +08:00

298 lines
9.2 KiB
Go
Raw Normal View History

MF-1248 - Add access policies for users (#1246) * authz service init Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * authz service init Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * add proto Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * add proto Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * authorize method Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * add casbib Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * add casbib Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * add casbib Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * add policy grpc Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * add policy grpc Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * fix db connection, add environment variables, docker-compose Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * add model.conf policy Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * add model.conf policy Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * fix test Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * adding tests, and token for auth Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * revert changes Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * fix auth service Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * small changes, add model conf env var Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * remove users grpc for now Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * fix error Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * some cosmetics Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * resolvin minor comments Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * change endpoint, add file to fix test Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * fix docker env Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com> * fix alignment and path for model.conf Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>
2020-12-08 17:04:09 +01:00
package main
import (
"fmt"
"io"
"io/ioutil"
"log"
"net"
"net/http"
"os"
"os/signal"
"syscall"
"time"
pgadapter "github.com/casbin/casbin-pg-adapter"
"github.com/casbin/casbin/v2"
kitprometheus "github.com/go-kit/kit/metrics/prometheus"
"github.com/mainflux/mainflux"
authapi "github.com/mainflux/mainflux/authn/api/grpc"
"github.com/mainflux/mainflux/authn/postgres"
"github.com/mainflux/mainflux/authz"
"github.com/mainflux/mainflux/authz/api"
grpcapi "github.com/mainflux/mainflux/authz/api/grpc"
httpapi "github.com/mainflux/mainflux/authz/api/http"
"github.com/mainflux/mainflux/authz/api/pb"
"github.com/mainflux/mainflux/logger"
localusers "github.com/mainflux/mainflux/things/users"
"github.com/opentracing/opentracing-go"
stdprometheus "github.com/prometheus/client_golang/prometheus"
jconfig "github.com/uber/jaeger-client-go/config"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
)
const (
defLogLevel = "error"
defDBHost = "localhost"
defDBPort = "5432"
defDBUser = "mainflux"
defDBPass = "mainflux"
defDB = "authz"
defDBSSLMode = "disable"
defDBSSLCert = ""
defDBSSLKey = ""
defDBSSLRootCert = ""
defHTTPPort = "8189"
defGRPCPort = "8187"
defClientTLS = "false"
defCACerts = ""
defSingleUserEmail = ""
defSingleUserToken = ""
defServerCert = ""
defServerKey = ""
defJaegerURL = ""
defAuthnURL = "localhost:8181"
defAuthnTimeout = "1s"
defModelConf = "model.conf"
envLogLevel = "MF_AUTHZ_LOG_LEVEL"
envDBHost = "MF_AUTHZ_DB_HOST"
envDBPort = "MF_AUTHZ_DB_PORT"
envDBUser = "MF_AUTHZ_DB_USER"
envDBPass = "MF_AUTHZ_DB_PASS"
envDB = "MF_AUTHZ_DB"
envModelConf = "MF_AUTHZ_MODEL_CONF"
envDBSSLMode = "MF_AUTHZ_DB_SSL_MODE"
envDBSSLCert = "MF_AUTHZ_DB_SSL_CERT"
envDBSSLKey = "MF_AUTHZ_DB_SSL_KEY"
envDBSSLRootCert = "MF_AUTHZ_DB_SSL_ROOT_CERT"
envClientTLS = "MF_AUTHZ_CLIENT_TLS"
envCACerts = "MF_AUTHZ_CA_CERTS"
envHTTPPort = "MF_AUTHZ_HTTP_PORT"
envGRPCPort = "MF_AUTHZ_GRPC_PORT"
envSingleUserEmail = "MF_AUTHZ_SINGLE_USER_EMAIL"
envSingleUserToken = "MF_AUTHZ_SINGLE_USER_TOKEN"
envServerCert = "MF_AUTHZ_SERVER_CERT"
envServerKey = "MF_AUTHZ_SERVER_KEY"
envJaegerURL = "MF_JAEGER_URL"
envAuthnURL = "MF_AUTHN_GRPC_URL"
envAuthnTimeout = "MF_AUTHN_GRPC_TIMEOUT"
)
type config struct {
logLevel string
dbConfig postgres.Config
httpPort string
grpcPort string
secret string
serverCert string
serverKey string
jaegerURL string
resetURL string
clientTLS bool
caCerts string
modelConf string
singleUserToken string
singleUserEmail string
authnURL string
authnTimeout time.Duration
}
func main() {
cfg := loadConfig()
logger, err := logger.New(os.Stdout, cfg.logLevel)
if err != nil {
log.Fatalf(err.Error())
}
dbcfg := cfg.dbConfig
conn := fmt.Sprintf(`postgresql://%s:%s@%s:%s/%s?sslmode=disable`, dbcfg.User, dbcfg.Pass, dbcfg.Host, dbcfg.Port, dbcfg.Name)
adapter, err := pgadapter.NewAdapter(conn)
if err != nil {
log.Fatalf(err.Error())
}
authTracer, authCloser := initJaeger("auth", cfg.jaegerURL, logger)
defer authCloser.Close()
auth, close := createAuthClient(cfg, authTracer, logger)
if close != nil {
defer close()
}
enf, err := casbin.NewSyncedEnforcer(cfg.modelConf, adapter)
if err != nil {
log.Fatalf(err.Error())
}
enf.EnableAutoSave(true)
tracer, closer := initJaeger("authz", cfg.jaegerURL, logger)
defer closer.Close()
svc := newService(enf, auth, logger)
errs := make(chan error, 2)
go startHTTPServer(tracer, svc, cfg.httpPort, cfg.serverCert, cfg.serverKey, logger, errs)
go startGRPCServer(tracer, svc, cfg.grpcPort, cfg.serverCert, cfg.serverKey, logger, errs)
go func() {
c := make(chan os.Signal)
signal.Notify(c, syscall.SIGINT)
errs <- fmt.Errorf("%s", <-c)
}()
err = <-errs
logger.Error(fmt.Sprintf("Authorization service terminated: %s", err))
}
func loadConfig() config {
authnTimeout, err := time.ParseDuration(mainflux.Env(envAuthnTimeout, defAuthnTimeout))
if err != nil {
log.Fatalf("Invalid %s value: %s", envAuthnTimeout, err.Error())
}
dbConfig := postgres.Config{
Host: mainflux.Env(envDBHost, defDBHost),
Port: mainflux.Env(envDBPort, defDBPort),
User: mainflux.Env(envDBUser, defDBUser),
Pass: mainflux.Env(envDBPass, defDBPass),
Name: mainflux.Env(envDB, defDB),
SSLMode: mainflux.Env(envDBSSLMode, defDBSSLMode),
SSLCert: mainflux.Env(envDBSSLCert, defDBSSLCert),
SSLKey: mainflux.Env(envDBSSLKey, defDBSSLKey),
SSLRootCert: mainflux.Env(envDBSSLRootCert, defDBSSLRootCert),
}
return config{
logLevel: mainflux.Env(envLogLevel, defLogLevel),
dbConfig: dbConfig,
httpPort: mainflux.Env(envHTTPPort, defHTTPPort),
grpcPort: mainflux.Env(envGRPCPort, defGRPCPort),
serverCert: mainflux.Env(envServerCert, defServerCert),
serverKey: mainflux.Env(envServerKey, defServerKey),
jaegerURL: mainflux.Env(envJaegerURL, defJaegerURL),
authnURL: mainflux.Env(envAuthnURL, defAuthnURL),
modelConf: mainflux.Env(envModelConf, defModelConf),
authnTimeout: authnTimeout,
}
}
func initJaeger(svcName, url string, logger logger.Logger) (opentracing.Tracer, io.Closer) {
if url == "" {
return opentracing.NoopTracer{}, ioutil.NopCloser(nil)
}
tracer, closer, err := jconfig.Configuration{
ServiceName: svcName,
Sampler: &jconfig.SamplerConfig{
Type: "const",
Param: 1,
},
Reporter: &jconfig.ReporterConfig{
LocalAgentHostPort: url,
LogSpans: true,
},
}.NewTracer()
if err != nil {
logger.Error(fmt.Sprintf("Failed to init Jaeger: %s", err))
os.Exit(1)
}
return tracer, closer
}
func createAuthClient(cfg config, tracer opentracing.Tracer, logger logger.Logger) (mainflux.AuthNServiceClient, func() error) {
if cfg.singleUserEmail != "" && cfg.singleUserToken != "" {
return localusers.NewSingleUserService(cfg.singleUserEmail, cfg.singleUserToken), nil
}
conn := connectToAuth(cfg, logger)
return authapi.NewClient(tracer, conn, cfg.authnTimeout), conn.Close
}
func connectToAuth(cfg config, logger logger.Logger) *grpc.ClientConn {
var opts []grpc.DialOption
if cfg.clientTLS {
if cfg.caCerts != "" {
tpc, err := credentials.NewClientTLSFromFile(cfg.caCerts, "")
if err != nil {
logger.Error(fmt.Sprintf("Failed to create tls credentials: %s", err))
os.Exit(1)
}
opts = append(opts, grpc.WithTransportCredentials(tpc))
}
} else {
opts = append(opts, grpc.WithInsecure())
logger.Info("gRPC communication is not encrypted")
}
conn, err := grpc.Dial(cfg.authnURL, opts...)
if err != nil {
logger.Error(fmt.Sprintf("Failed to connect to authn service: %s", err))
os.Exit(1)
}
return conn
}
func newService(enf *casbin.SyncedEnforcer, auth mainflux.AuthNServiceClient, logger logger.Logger) authz.Service {
svc := authz.New(enf, auth)
svc = api.LoggingMiddleware(svc, logger)
svc = api.MetricsMiddleware(
svc,
kitprometheus.NewCounterFrom(stdprometheus.CounterOpts{
Namespace: "authz",
Subsystem: "api",
Name: "request_count",
Help: "Number of requests received.",
}, []string{"method"}),
kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{
Namespace: "authz",
Subsystem: "api",
Name: "request_latency_microseconds",
Help: "Total duration of requests in microseconds.",
}, []string{"method"}),
)
return svc
}
func startHTTPServer(tracer opentracing.Tracer, svc authz.Service, port string, certFile string, keyFile string, logger logger.Logger, errs chan error) {
p := fmt.Sprintf(":%s", port)
if certFile != "" || keyFile != "" {
logger.Info(fmt.Sprintf("Authorization service started using https, cert %s key %s, exposed port %s", certFile, keyFile, port))
errs <- http.ListenAndServeTLS(p, certFile, keyFile, httpapi.MakeHandler(svc, tracer))
return
}
logger.Info(fmt.Sprintf("Authorization service started using http, exposed port %s", port))
errs <- http.ListenAndServe(p, httpapi.MakeHandler(svc, tracer))
}
func startGRPCServer(tracer opentracing.Tracer, svc authz.Service, port string, certFile string, keyFile string, logger logger.Logger, errs chan error) {
p := fmt.Sprintf(":%s", port)
listener, err := net.Listen("tcp", p)
if err != nil {
logger.Error(fmt.Sprintf("Failed to listen on port %s: %s", port, err))
}
var server *grpc.Server
if certFile != "" || keyFile != "" {
creds, err := credentials.NewServerTLSFromFile(certFile, keyFile)
if err != nil {
logger.Error(fmt.Sprintf("Failed to load authz certificates: %s", err))
os.Exit(1)
}
logger.Info(fmt.Sprintf("Authorization gRPC service started using https on port %s with cert %s key %s", port, certFile, keyFile))
server = grpc.NewServer(grpc.Creds(creds))
} else {
logger.Info(fmt.Sprintf("Authorization gRPC service started using http on port %s", port))
server = grpc.NewServer()
}
pb.RegisterAuthZServiceServer(server, grpcapi.NewServer(tracer, svc))
logger.Info(fmt.Sprintf("Authorization gRPC service started, exposed port %s", port))
errs <- server.Serve(listener)
}