Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MF-1008 - Make token duration configurable #1550

Merged
merged 14 commits into from
Jan 25, 2022
39 changes: 20 additions & 19 deletions auth/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,24 +55,25 @@ The service is configured using the environment variables presented in the
following table. Note that any unset variables will be replaced with their
default values.

| Variable | Description | Default |
|---------------------------|--------------------------------------------------------------------------|---------------|
| MF_AUTH_LOG_LEVEL | Service level (debug, info, warn, error) | error |
| MF_AUTH_DB_HOST | Database host address | localhost |
| MF_AUTH_DB_PORT | Database host port | 5432 |
| MF_AUTH_DB_USER | Database user | mainflux |
| MF_AUTH_DB_PASSWORD | Database password | mainflux |
| MF_AUTH_DB | Name of the database used by the service | auth |
| MF_AUTH_DB_SSL_MODE | Database connection SSL mode (disable, require, verify-ca, verify-full) | disable |
| MF_AUTH_DB_SSL_CERT | Path to the PEM encoded certificate file | |
| MF_AUTH_DB_SSL_KEY | Path to the PEM encoded key file | |
| MF_AUTH_DB_SSL_ROOT_CERT | Path to the PEM encoded root certificate file | |
| MF_AUTH_HTTP_PORT | Auth service HTTP port | 8180 |
| MF_AUTH_GRPC_PORT | Auth service gRPC port | 8181 |
| MF_AUTH_SERVER_CERT | Path to server certificate in pem format | |
| MF_AUTH_SERVER_KEY | Path to server key in pem format | |
| MF_AUTH_SECRET | String used for signing tokens | auth |
| MF_JAEGER_URL | Jaeger server URL | localhost:6831|
| Variable | Description | Default |
|-------------------------------|--------------------------------------------------------------------------|----------------|
| MF_AUTH_LOG_LEVEL | Service level (debug, info, warn, error) | error |
| MF_AUTH_DB_HOST | Database host address | localhost |
| MF_AUTH_DB_PORT | Database host port | 5432 |
| MF_AUTH_DB_USER | Database user | mainflux |
| MF_AUTH_DB_PASSWORD | Database password | mainflux |
| MF_AUTH_DB | Name of the database used by the service | auth |
| MF_AUTH_DB_SSL_MODE | Database connection SSL mode (disable, require, verify-ca, verify-full) | disable |
| MF_AUTH_DB_SSL_CERT | Path to the PEM encoded certificate file | |
| MF_AUTH_DB_SSL_KEY | Path to the PEM encoded key file | |
| MF_AUTH_DB_SSL_ROOT_CERT | Path to the PEM encoded root certificate file | |
| MF_AUTH_HTTP_PORT | Auth service HTTP port | 8180 |
| MF_AUTH_GRPC_PORT | Auth service gRPC port | 8181 |
| MF_AUTH_SERVER_CERT | Path to server certificate in pem format | |
| MF_AUTH_SERVER_KEY | Path to server key in pem format | |
| MF_AUTH_SECRET | String used for signing tokens | auth |
| MF_AUTH_LOGIN_TOKEN_DURATION | The login token expiration period | 10h |
| MF_JAEGER_URL | Jaeger server URL | localhost:6831 |

## Deployment

Expand All @@ -95,7 +96,7 @@ make auth
make install

# set the environment variables and run the service
MF_AUTH_LOG_LEVEL=[Service log level] MF_AUTH_DB_HOST=[Database host address] MF_AUTH_DB_PORT=[Database host port] MF_AUTH_DB_USER=[Database user] MF_AUTH_DB_PASS=[Database password] MF_AUTH_DB=[Name of the database used by the service] MF_AUTH_DB_SSL_MODE=[SSL mode to connect to the database with] MF_AUTH_DB_SSL_CERT=[Path to the PEM encoded certificate file] MF_AUTH_DB_SSL_KEY=[Path to the PEM encoded key file] MF_AUTH_DB_SSL_ROOT_CERT=[Path to the PEM encoded root certificate file] MF_AUTH_HTTP_PORT=[Service HTTP port] MF_AUTH_GRPC_PORT=[Service gRPC port] MF_AUTH_SECRET=[String used for signing tokens] MF_AUTH_SERVER_CERT=[Path to server certificate] MF_AUTH_SERVER_KEY=[Path to server key] MF_JAEGER_URL=[Jaeger server URL] $GOBIN/mainflux-auth
MF_AUTH_LOG_LEVEL=[Service log level] MF_AUTH_DB_HOST=[Database host address] MF_AUTH_DB_PORT=[Database host port] MF_AUTH_DB_USER=[Database user] MF_AUTH_DB_PASS=[Database password] MF_AUTH_DB=[Name of the database used by the service] MF_AUTH_DB_SSL_MODE=[SSL mode to connect to the database with] MF_AUTH_DB_SSL_CERT=[Path to the PEM encoded certificate file] MF_AUTH_DB_SSL_KEY=[Path to the PEM encoded key file] MF_AUTH_DB_SSL_ROOT_CERT=[Path to the PEM encoded root certificate file] MF_AUTH_HTTP_PORT=[Service HTTP port] MF_AUTH_GRPC_PORT=[Service gRPC port] MF_AUTH_SECRET=[String used for signing tokens] MF_AUTH_SERVER_CERT=[Path to server certificate] MF_AUTH_SERVER_KEY=[Path to server key] MF_JAEGER_URL=[Jaeger server URL] MF_AUTH_LOGIN_TOKEN_DURATION=[The login token expiration period] $GOBIN/mainflux-auth
```

If `MF_EMAIL_TEMPLATE` doesn't point to any file service will function but password reset functionality will not work.
Expand Down
3 changes: 2 additions & 1 deletion auth/api/grpc/endpoint_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ const (

authoritiesObj = "authorities"
memberRelation = "member"
loginDuration = 30 * time.Minute
)

var svc auth.Service
Expand All @@ -52,7 +53,7 @@ func newService() auth.Service {

t := jwt.New(secret)

return auth.New(repo, groupRepo, idProvider, t, ketoMock)
return auth.New(repo, groupRepo, idProvider, t, ketoMock, loginDuration)
}

func startGRPCServer(svc auth.Service, port int) {
Expand Down
11 changes: 6 additions & 5 deletions auth/api/http/groups/endpoint_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,11 @@ import (
)

const (
contentType = "application/json"
email = "user@example.com"
secret = "secret"
id = "testID"
contentType = "application/json"
email = "user@example.com"
secret = "secret"
id = "testID"
loginDuration = 30 * time.Minute
)

type testRequest struct {
Expand Down Expand Up @@ -59,7 +60,7 @@ func newService() auth.Service {
idProvider := uuid.NewMock()
t := jwt.New(secret)
policies := mocks.NewKetoMock(map[string][]mocks.MockSubjectSet{})
return auth.New(keys, groups, idProvider, t, policies)
return auth.New(keys, groups, idProvider, t, policies, loginDuration)
}

func newServer(svc auth.Service) *httptest.Server {
Expand Down
11 changes: 6 additions & 5 deletions auth/api/http/keys/endpoint_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,11 @@ import (
)

const (
secret = "secret"
contentType = "application/json"
id = "123e4567-e89b-12d3-a456-000000000001"
email = "user@example.com"
secret = "secret"
contentType = "application/json"
id = "123e4567-e89b-12d3-a456-000000000001"
email = "user@example.com"
loginDuration = 30 * time.Minute
)

type issueRequest struct {
Expand Down Expand Up @@ -70,7 +71,7 @@ func newService() auth.Service {
mockAuthzDB[id] = append(mockAuthzDB[id], mocks.MockSubjectSet{Object: "authorities", Relation: "member"})
ketoMock := mocks.NewKetoMock(mockAuthzDB)

return auth.New(repo, groupRepo, idProvider, t, ketoMock)
return auth.New(repo, groupRepo, idProvider, t, ketoMock, loginDuration)
}

func newServer(svc auth.Service) *httptest.Server {
Expand Down
15 changes: 8 additions & 7 deletions auth/api/http/policies/endpoint_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,13 @@ import (
)

const (
secret = "secret"
contentType = "application/json"
id = uuid.Prefix + "-000000000001"
email = "user@example.com"
unauthzID = uuid.Prefix + "-000000000002"
unauthzEmail = "unauthz@example.com"
secret = "secret"
contentType = "application/json"
id = uuid.Prefix + "-000000000001"
email = "user@example.com"
unauthzID = uuid.Prefix + "-000000000002"
unauthzEmail = "unauthz@example.com"
loginDuration = 30 * time.Minute
)

type testRequest struct {
Expand Down Expand Up @@ -68,7 +69,7 @@ func newService() auth.Service {
mockAuthzDB[unauthzID] = append(mockAuthzDB[unauthzID], mocks.MockSubjectSet{Object: "users", Relation: "member"})
ketoMock := mocks.NewKetoMock(mockAuthzDB)

return auth.New(repo, groupRepo, idProvider, t, ketoMock)
return auth.New(repo, groupRepo, idProvider, t, ketoMock, loginDuration)
}

func newServer(svc auth.Service) *httptest.Server {
Expand Down
34 changes: 17 additions & 17 deletions auth/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,8 @@ import (
)

const (
loginDuration = 10 * time.Hour
recoveryDuration = 5 * time.Minute
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't remove recoveryDuration, please keep using the constant for recovery duration.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It wasn't being used but I have kept it back


thingsGroupType = "things"
thingsGroupType = "things"

authoritiesObject = "authorities"
memberRelation = "member"
Expand Down Expand Up @@ -102,23 +100,25 @@ type Service interface {
var _ Service = (*service)(nil)

type service struct {
keys KeyRepository
groups GroupRepository
idProvider mainflux.IDProvider
ulidProvider mainflux.IDProvider
agent PolicyAgent
tokenizer Tokenizer
keys KeyRepository
groups GroupRepository
idProvider mainflux.IDProvider
ulidProvider mainflux.IDProvider
agent PolicyAgent
tokenizer Tokenizer
loginDuration time.Duration
}

// New instantiates the auth service implementation.
func New(keys KeyRepository, groups GroupRepository, idp mainflux.IDProvider, tokenizer Tokenizer, policyAgent PolicyAgent) Service {
func New(keys KeyRepository, groups GroupRepository, idp mainflux.IDProvider, tokenizer Tokenizer, policyAgent PolicyAgent, duration time.Duration) Service {
return &service{
tokenizer: tokenizer,
keys: keys,
groups: groups,
idProvider: idp,
ulidProvider: ulid.New(),
agent: policyAgent,
tokenizer: tokenizer,
keys: keys,
groups: groups,
idProvider: idp,
ulidProvider: ulid.New(),
agent: policyAgent,
loginDuration: duration,
}
}

Expand All @@ -132,7 +132,7 @@ func (svc service) Issue(ctx context.Context, token string, key Key) (Key, strin
case RecoveryKey:
return svc.tmpKey(recoveryDuration, key)
default:
return svc.tmpKey(loginDuration, key)
return svc.tmpKey(svc.loginDuration, key)
dborovcanin marked this conversation as resolved.
Show resolved Hide resolved
}
}

Expand Down
3 changes: 2 additions & 1 deletion auth/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ const (

memberRelation = "member"
authoritiesObj = "authorities"
loginDuration = 30 * time.Minute
)

func newService() auth.Service {
Expand All @@ -41,7 +42,7 @@ func newService() auth.Service {
ketoMock := mocks.NewKetoMock(mockAuthzDB)

t := jwt.New(secret)
return auth.New(repo, groupRepo, idProvider, t, ketoMock)
return auth.New(repo, groupRepo, idProvider, t, ketoMock, loginDuration)
}

func TestIssue(t *testing.T) {
Expand Down
16 changes: 13 additions & 3 deletions cmd/auth/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"os"
"os/signal"
"syscall"
"time"

kitprometheus "github.com/go-kit/kit/metrics/prometheus"
"github.com/jmoiron/sqlx"
Expand Down Expand Up @@ -52,6 +53,7 @@ const (
defKetoHost = "mainflux-keto"
defKetoWritePort = "4467"
defKetoReadPort = "4466"
defLoginDuration = "10h"

envLogLevel = "MF_AUTH_LOG_LEVEL"
envDBHost = "MF_AUTH_DB_HOST"
Expand All @@ -72,6 +74,7 @@ const (
envKetoHost = "MF_KETO_HOST"
envKetoWritePort = "MF_KETO_WRITE_REMOTE_PORT"
envKetoReadPort = "MF_KETO_READ_REMOTE_PORT"
envLoginDuration = "MF_AUTH_LOGIN_TOKEN_DURATION"
)

type config struct {
Expand All @@ -87,6 +90,7 @@ type config struct {
ketoHost string
ketoWritePort string
ketoReadPort string
loginDuration time.Duration
}

type tokenConfig struct {
Expand All @@ -113,7 +117,7 @@ func main() {

readerConn, writerConn := initKeto(cfg.ketoHost, cfg.ketoReadPort, cfg.ketoWritePort, logger)

svc := newService(db, dbTracer, cfg.secret, logger, readerConn, writerConn)
svc := newService(db, dbTracer, cfg.secret, logger, readerConn, writerConn, cfg.loginDuration)
errs := make(chan error, 2)

go startHTTPServer(tracer, svc, cfg.httpPort, cfg.serverCert, cfg.serverKey, logger, errs)
Expand Down Expand Up @@ -142,6 +146,11 @@ func loadConfig() config {
SSLRootCert: mainflux.Env(envDBSSLRootCert, defDBSSLRootCert),
}

loginDuration, err := time.ParseDuration(mainflux.Env(envLoginDuration, defLoginDuration))
Copy link
Contributor

@arvindh123 arvindh123 Jan 24, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of panic can we use defLoginDuration value
And log the envLoginDuration as warning or error
If the defLoginDuration value got error during parse , then log panic

Like

loginDuration, err := time.ParseDuration(envLoginDuration)
if err != nil {
	logger.Info("Invalid Login Duration %v: %v", envLoginDuration, err)
	loginDuration , err := time.ParseDuration(defLoginDuration)
	if err != nil  {
               log.Painc(fmt.Sprintf("Invalid default login duration %v: %v", defLoginDuration err))
       }
       	logger.Info("Using default Login Duration %v", defLoginDuration)
}	

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's is one way to do it. TBH, since default is not exactly fallback (despite the params naming in mainflux.Env), and due to code simplicity, I prefer the current solution. It's also aligned with the rest of the codebase.

if err != nil {
log.Fatal(err)
}

return config{
logLevel: mainflux.Env(envLogLevel, defLogLevel),
dbConfig: dbConfig,
Expand All @@ -154,6 +163,7 @@ func loadConfig() config {
ketoHost: mainflux.Env(envKetoHost, defKetoHost),
ketoReadPort: mainflux.Env(envKetoReadPort, defKetoReadPort),
ketoWritePort: mainflux.Env(envKetoWritePort, defKetoWritePort),
loginDuration: loginDuration,
}

}
Expand Down Expand Up @@ -207,7 +217,7 @@ func connectToDB(dbConfig postgres.Config, logger logger.Logger) *sqlx.DB {
return db
}

func newService(db *sqlx.DB, tracer opentracing.Tracer, secret string, logger logger.Logger, readerConn, writerConn *grpc.ClientConn) auth.Service {
func newService(db *sqlx.DB, tracer opentracing.Tracer, secret string, logger logger.Logger, readerConn, writerConn *grpc.ClientConn, duration time.Duration) auth.Service {
database := postgres.NewDatabase(db)
keysRepo := tracing.New(postgres.New(database), tracer)

Expand All @@ -219,7 +229,7 @@ func newService(db *sqlx.DB, tracer opentracing.Tracer, secret string, logger lo
idProvider := uuid.New()
t := jwt.New(secret)

svc := auth.New(keysRepo, groupsRepo, idProvider, t, pa)
svc := auth.New(keysRepo, groupsRepo, idProvider, t, pa, duration)
svc = api.LoggingMiddleware(svc, logger)
svc = api.MetricsMiddleware(
svc,
Expand Down
1 change: 1 addition & 0 deletions docker/.env
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ MF_AUTH_DB_USER=mainflux
MF_AUTH_DB_PASS=mainflux
MF_AUTH_DB=auth
MF_AUTH_SECRET=secret
MF_AUTH_LOGIN_TOKEN_DURATION="10h"

### Keto
MF_KETO_HOST=mainflux-keto
Expand Down
1 change: 1 addition & 0 deletions docker/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ services:
MF_AUTH_HTTP_PORT: ${MF_AUTH_HTTP_PORT}
MF_AUTH_GRPC_PORT: ${MF_AUTH_GRPC_PORT}
MF_AUTH_SECRET: ${MF_AUTH_SECRET}
MF_AUTH_LOGIN_TOKEN_DURATION: ${MF_AUTH_LOGIN_TOKEN_DURATION}
MF_JAEGER_URL: ${MF_JAEGER_URL}
MF_KETO_HOST: ${MF_KETO_HOST}
MF_KETO_WRITE_REMOTE_PORT: ${MF_KETO_WRITE_REMOTE_PORT}
Expand Down
2 changes: 1 addition & 1 deletion scripts/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ MF_COAP_ADAPTER_LOG_LEVEL=info MF_COAP_ADAPTER_PORT=5683 MF_THINGS_AUTH_GRPC_URL
###
# AUTH
###
MF_AUTH_LOG_LEVEL=debug MF_AUTH_HTTP_PORT=8189 MF_AUTH_GRPC_PORT=8181 MF_AUTH_DB_PORT=5432 MF_AUTH_DB_USER=mainflux MF_AUTH_DB_PASS=mainflux MF_AUTH_DB=auth MF_AUTH_SECRET=secret $BUILD_DIR/mainflux-auth &
MF_AUTH_LOG_LEVEL=debug MF_AUTH_HTTP_PORT=8189 MF_AUTH_GRPC_PORT=8181 MF_AUTH_DB_PORT=5432 MF_AUTH_DB_USER=mainflux MF_AUTH_DB_PASS=mainflux MF_AUTH_DB=auth MF_AUTH_SECRET=secret MF_AUTH_LOGIN_TOKEN_DURATION=10h $BUILD_DIR/mainflux-auth &

trap cleanup EXIT

Expand Down