~mna/straw

7dd22fb4315a74e63cbeb6839d94ad112c286b61 — Martin Angers 1 year, 4 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 @@ alias_attributes = ["email"]
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"
}