7dd22fb4315a74e63cbeb6839d94ad112c286b61 — Martin Angers 8 months ago b020920
pkg/service/auth: start implementing the auth service
4 files changed, 203 insertions(+), 0 deletions(-)

M doc/auth.toml
A pkg/service/auth/config.go
A pkg/service/auth/resource.go
A pkg/service/auth/service.go
M doc/auth.toml => doc/auth.toml +4 -0
@@ 1,4 1,6 @@ # TODO: no SMS support for now
+ # TODO: no identity provider support for now
+ # TODO: no multiple clients support for now
  
  name = "pool_name"
  mfa = "on|off|optional"


@@ 6,6 8,8 @@ username_attributes = []
  auto_verified_attributes = []
  
+ client_name = "defaults to app"
+ 
  [verification_message]
    default_email_option = "CONFIRM_WITH_LINK"
    email_message = ""

A pkg/service/auth/config.go => pkg/service/auth/config.go +101 -0
@@ 0,0 1,101 @@
+ package auth
+ 
+ import (
+ 	"path/filepath"
+ 
+ 	"git.sr.ht/~mna/straw"
+ 	"git.sr.ht/~mna/straw/pkg/service"
+ 
+ 	"github.com/BurntSushi/toml"
+ )
+ 
+ type config struct {
+ 	Name              string            `toml:"name"`
+ 	MFA               string            `toml:"mfa"`
+ 	AliasAttrs        []string          `toml:"alias_attributes"`
+ 	UsernameAttrs     []string          `toml:"username_attributes"`
+ 	AutoVerifiedAttrs []string          `toml:"auto_verified_attributes"`
+ 	ClientName        string            `toml:"client_name"`
+ 	Tags              map[string]string `toml:"tags"`
+ 
+ 	VerificationMessage struct {
+ 		DefaultEmailOption string `toml:"default_email_option"`
+ 		EmailMessage       string `toml:"email_message"`
+ 		EmailSubject       string `toml:"email_subject"`
+ 	} `toml:"verification_message"`
+ 
+ 	Admin struct {
+ 		AllowAdminCreateUserOnly bool   `toml:"allow_admin_create_user_only"`
+ 		UnusedAccountValidity    string `toml:"unused_account_validity"`
+ 		InviteEmailMessage       string `toml:"invite_email_message"`
+ 		InviteEmailSubject       string `toml:"invite_email_subject"`
+ 	} `toml:"admin"`
+ 
+ 	Device struct {
+ 		ChallengeRequiredOnNewDevice     bool `toml:"challenge_required_on_new_device"`
+ 		DeviceOnlyRememberedOnUserPrompt bool `toml:"device_only_remembered_on_user_prompt"`
+ 	} `toml:"device"`
+ 
+ 	PasswordPolicy struct {
+ 		MinLength        int  `toml:"minimum_length"`
+ 		RequireLowercase bool `toml:"require_lowercase"`
+ 		RequireNumbers   bool `toml:"require_numbers"`
+ 		RequireSymbols   bool `toml:"require_symbols"`
+ 		RequireUppercase bool `toml:"require_uppercase"`
+ 	} `toml:"password_policy"`
+ 
+ 	Triggers struct {
+ 		CreateAuthChallenge         string `toml:"create_auth_challenge"`
+ 		CustomMessage               string `toml:"custom_message"`
+ 		DefineAuthChallenge         string `toml:"define_auth_challenge"`
+ 		PostAuthentication          string `toml:"post_authentication"`
+ 		PostConfirmation            string `toml:"post_confirmation"`
+ 		PreAuthentication           string `toml:"pre_authentication"`
+ 		PreSignUp                   string `toml:"pre_sign_up"`
+ 		PreTokenGeneration          string `toml:"pre_token_generation"`
+ 		UserMigration               string `toml:"user_migration"`
+ 		VerifyAuthChallengeResponse string `toml:"verify_auth_challenge_response"`
+ 	} `toml:"triggers"`
+ 
+ 	SchemaAttrs []struct {
+ 		DataType      string `toml:"data_type"`
+ 		DeveloperOnly bool   `toml:"developer_only"`
+ 		Mutable       bool   `toml:"mutable"`
+ 		Name          string `toml:"string"`
+ 		Required      bool   `toml:"required"`
+ 		MinLength     int    `toml:"min_length"`
+ 		MaxLength     int    `toml:"max_length"`
+ 		MinValue      int    `toml:"min_value"`
+ 		MaxValue      int    `toml:"max_value"`
+ 	} `toml:"schema_attributes"`
+ }
+ 
+ func (s *Service) newAuth(sc service.ScanConfig) straw.Resource {
+ 	baseDir := filepath.Base(sc.Src)
+ 	return &Resource{
+ 		ID:          baseDir,
+ 		Name:        baseDir,
+ 		StagePrefix: true,
+ 		ClientName:  s.RC.AppName,
+ 
+ 		service: s,
+ 	}
+ }
+ 
+ func (s *Service) applyAuthConfig(sc service.ScanConfig, res straw.Resource, file string) error {
+ 	var c config
+ 	meta, err := toml.DecodeFile(file, &c)
+ 	if err != nil {
+ 		return err
+ 	}
+ 
+ 	a := res.(*Resource)
+ 	if meta.IsDefined("name") {
+ 		a.Name = c.Name
+ 	}
+ 	// TODO: complete configuration...
+ 	if meta.IsDefined("tags") {
+ 		a.Tags = c.Tags
+ 	}
+ 	return nil
+ }

A pkg/service/auth/resource.go => pkg/service/auth/resource.go +22 -0
@@ 0,0 1,22 @@
+ package auth
+ 
+ import (
+ 	"git.sr.ht/~mna/straw"
+ )
+ 
+ var (
+ 	_ straw.Resource  = (*Resource)(nil)
+ 	_ straw.Validator = (*Resource)(nil)
+ )
+ 
+ // Resource represents an auth resource.
+ type Resource struct {
+ 	ID          string
+ 	Name        string
+ 	StagePrefix bool
+ 	Tags        map[string]string
+ 
+ 	// TODO: complete...
+ 
+ 	service *Service
+ }

A pkg/service/auth/service.go => pkg/service/auth/service.go +76 -0
@@ 0,0 1,76 @@
+ package auth
+ 
+ import (
+ 	"git.sr.ht/~mna/straw"
+ 	"git.sr.ht/~mna/straw/pkg/service"
+ 	"git.sr.ht/~mna/straw/pkg/template"
+ )
+ 
+ var (
+ 	_ straw.Service   = (*Service)(nil)
+ 	_ straw.Generator = (*Service)(nil)
+ )
+ 
+ var envVarMapping = map[string]string{
+ 	"name": "name",
+ 	"arn":  "arn",
+ 	"id":   "id",
+ }
+ 
+ // Service is the service implementation for auth.
+ type Service struct {
+ 	RC straw.RunConfig
+ 	CR *straw.CollectedResources
+ }
+ 
+ // EnvVarMapping returns the mapping of straw attributes to
+ // terraform attributes.
+ func (s *Service) EnvVarMapping() map[string]string {
+ 	return envVarMapping
+ }
+ 
+ // Principal returns the policy principal for the auth
+ // service.
+ func (s *Service) Principal() *straw.PolicyPrincipal {
+ 	return &straw.PolicyPrincipal{
+ 		Type:        "Service",
+ 		Identifiers: []string{"cognito-idp.amazonaws.com"},
+ 	}
+ }
+ 
+ // Scan generates resources for this service by reading the
+ // provided src directory.
+ func (s *Service) Scan(src, dist string) error {
+ 	rs := &service.ResourceScanner{
+ 		ConfigFiles: s.RC.ConfigFiles,
+ 		NewFunc:     s.newAuth,
+ 		ApplyFunc:   s.applyAuthConfig,
+ 	}
+ 	sc := service.ScanConfig{
+ 		Src:   src,
+ 		Dist:  dist,
+ 		Stage: s.RC.Stage,
+ 	}
+ 	res, err := rs.Scan(sc)
+ 	if len(res) > 0 {
+ 		s.CR.Resources = append(s.CR.Resources, res...)
+ 		s.CR.Generators = append(s.CR.Generators, s)
+ 	}
+ 	return err
+ }
+ 
+ // Generate implements a straw.Generator for the auth.
+ // It generates the auth config.
+ func (s *Service) Generate(dir string, reg *straw.Registries) error {
+ 	t := template.New("auth.tf.tpl").
+ 		FilteredResources("Auths", s.CR.Resources, s.Type()).
+ 		ResourcesMap(reg.Resources).
+ 		EnvMap(reg.Environment)
+ 
+ 	return t.Execute(dir)
+ }
+ 
+ // Type returns the type name of this service.
+ func (s *Service) Type() string {
+ 	return "auth"
+ }