Skip to content

Commit

Permalink
feat: email based authentication and invites to an org (#248)
Browse files Browse the repository at this point in the history
- mail configuration is available as smtp in app config
- invite email body is not configurable at this point

Signed-off-by: Kush Sharma <thekushsharma@gmail.com>
  • Loading branch information
kushsharma committed May 31, 2023
1 parent 93bb170 commit 09bb80c
Show file tree
Hide file tree
Showing 78 changed files with 10,347 additions and 4,976 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ GOVERSION := $(shell go version | cut -d ' ' -f 3 | cut -d '.' -f 2)

.PHONY: build check fmt lint test test-race vet test-cover-html help install proto ui
.DEFAULT_GOAL := build
PROTON_COMMIT := "fd6027f933a1cce9d6f25ef8d4ee4bba855959e4"
PROTON_COMMIT := "e383abda68a4543eaf09fa578ce8862465fdf3aa"

ui:
@echo " > generating ui build"
Expand Down
23 changes: 21 additions & 2 deletions cmd/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ import (
"syscall"
"time"

"github.com/odpf/shield/core/invitation"

"github.com/odpf/shield/pkg/mailer"

"github.com/odpf/shield/core/permission"
"github.com/odpf/shield/internal/bootstrap"

Expand Down Expand Up @@ -230,9 +234,23 @@ func buildAPIDependencies(
userService,
)

registrationService := authenticate.NewRegistrationService(logger, cfg.App.Authentication, postgres.NewFlowRepository(logger, dbc), userService)
var mailDialer mailer.Dialer = mailer.NewMockDialer()
if cfg.App.Mailer.SMTPHost != "" && cfg.App.Mailer.SMTPHost != "smtp.example.com" {
mailDialer = mailer.NewDialerImpl(cfg.App.Mailer.SMTPHost,
cfg.App.Mailer.SMTPPort,
cfg.App.Mailer.SMTPUsername,
cfg.App.Mailer.SMTPPassword,
cfg.App.Mailer.SMTPInsecure,
cfg.App.Mailer.Headers,
)
}
registrationService := authenticate.NewRegistrationService(logger, cfg.App.Authentication,
postgres.NewFlowRepository(logger, dbc), userService, mailDialer)

cascadeDeleter := deleter.NewCascadeDeleter(organizationService, projectService, resourceService, groupService, policyService, roleService)
invitationService := invitation.NewService(mailDialer, postgres.NewInvitationRepository(logger, dbc),
organizationService, groupService, userService, relationService)
cascadeDeleter := deleter.NewCascadeDeleter(organizationService, projectService, resourceService,
groupService, policyService, roleService, invitationService)

dependencies := api.Deps{
DisableOrgsListing: cfg.App.DisableOrgsListing,
Expand All @@ -252,6 +270,7 @@ func buildAPIDependencies(
DeleterService: cascadeDeleter,
MetaSchemaService: metaschemaService,
BootstrapService: bootstrapService,
InvitationService: invitationService,
}
return dependencies, nil
}
Expand Down
14 changes: 14 additions & 0 deletions config/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ app:
client_id: "xxxxx.apps.googleusercontent.com"
client_secret: "xxxxx"
issuer_url: "https://accounts.google.com"
validity: 10m
mail_otp:
subject: "Shield - Login Link"
body: "Please copy/paste the otp below to login.\n\n{{.Otp}}\n\nThis code will expire in 5 minutes."
validity: 10m
# platform level administration
admin:
# Email list of users which needs to be converted as superusers
Expand All @@ -59,6 +64,15 @@ app:
# UUIDs/slugs of existing users can also be provided instead of email ids
# but in that case a new user will not be created.
users: []
# smtp configuration for sending emails
mailer:
smtp_host: smtp.example.com
smtp_port: 587
smtp_username: "username"
smtp_password: "password"
smtp_insecure: true
headers:
from: "username@acme.org"
db:
driver: postgres
url: postgres://shield:@localhost:5432/shield?sslmode=disable
Expand Down
26 changes: 22 additions & 4 deletions core/authenticate/authenticate.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
type AuthMethod string

const (
MailAuthMethod AuthMethod = "mail"
MailOTPAuthMethod AuthMethod = "mailotp"
)

func (m AuthMethod) String() string {
Expand All @@ -25,6 +25,9 @@ type Flow struct {
// authentication flow type
Method string

// Email is the email of the user
Email string

// StartURL is where flow should start from for verification
StartURL string
// FinishURL is where flow should end to after successful verification
Expand All @@ -37,6 +40,13 @@ type Flow struct {

// CreatedAt will be used to clean-up dead auth flows
CreatedAt time.Time

// ExpiresAt is the time when the flow will expire
ExpiresAt time.Time
}

func (f Flow) IsValid() bool {
return f.ExpiresAt.Before(time.Now().UTC())
}

type Config struct {
Expand All @@ -46,6 +56,7 @@ type Config struct {
OIDCConfig map[string]OIDCConfig `yaml:"oidc_config" mapstructure:"oidc_config"`
Session SessionConfig `yaml:"session" mapstructure:"session"`
Token TokenConfig `yaml:"token" mapstructure:"token"`
MailOTP MailOTPConfig `yaml:"mail_otp" mapstructure:"mail_otp"`
}

type TokenConfig struct {
Expand All @@ -64,7 +75,14 @@ type SessionConfig struct {
}

type OIDCConfig struct {
ClientID string `yaml:"client_id" mapstructure:"client_id"`
ClientSecret string `yaml:"client_secret" mapstructure:"client_secret"`
IssuerUrl string `yaml:"issuer_url" mapstructure:"issuer_url"`
ClientID string `yaml:"client_id" mapstructure:"client_id"`
ClientSecret string `yaml:"client_secret" mapstructure:"client_secret"`
IssuerUrl string `yaml:"issuer_url" mapstructure:"issuer_url"`
Validity time.Duration `yaml:"validity" mapstructure:"validity" default:"15m"`
}

type MailOTPConfig struct {
Subject string `yaml:"subject" mapstructure:"subject" default:"Shield Login OTP"`
Body string `yaml:"body" mapstructure:"body" default:"Shield Login Link"`
Validity time.Duration `yaml:"validity" mapstructure:"validity" default:"10m"`
}

0 comments on commit 09bb80c

Please sign in to comment.