Skip to content

Commit

Permalink
Merge pull request #2 from seanturner026/update
Browse files Browse the repository at this point in the history
Added json logging, added admin user create on terraform apply
  • Loading branch information
seanturner026 authored May 7, 2021
2 parents a29a646 + cfa05cd commit 1348439
Show file tree
Hide file tree
Showing 42 changed files with 799 additions and 507 deletions.
Binary file modified assets/users.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 6 additions & 6 deletions cmd/auth/events.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
[
{
"resource": "/",
"path": "/users/login",
"path": "/auth/login",
"httpMethod": "POST",
"requestContext": {
"resourcePath": "/",
"httpMethod": "POST",
"path": "/users/login"
"path": "/auth/login"
},
"headers": {},
"multiValueHeaders": {},
Expand All @@ -19,12 +19,12 @@
},
{
"resource": "/",
"path": "/users/refresh/token",
"path": "/auth/refresh/token",
"httpMethod": "POST",
"requestContext": {
"resourcePath": "/",
"httpMethod": "POST",
"path": "/users/refresh/token"
"path": "/auth/refresh/token"
},
"headers": {},
"multiValueHeaders": {},
Expand All @@ -37,12 +37,12 @@
},
{
"resource": "/",
"path": "/users/reset/password",
"path": "/auth/reset/password",
"httpMethod": "POST",
"requestContext": {
"resourcePath": "/",
"httpMethod": "POST",
"path": "/users/reset/password"
"path": "/auth/reset/password"
},
"headers": {},
"multiValueHeaders": {},
Expand Down
55 changes: 27 additions & 28 deletions cmd/auth/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ package main
import (
"encoding/json"
"fmt"
"log"
"time"

"github.com/aws/aws-lambda-go/events"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
cidp "github.com/aws/aws-sdk-go/service/cognitoidentityprovider"
"github.com/aws/aws-sdk-go/service/cognitoidentityprovider"
"github.com/seanturner026/serverless-release-dashboard/internal/util"
log "github.com/sirupsen/logrus"
)

type userAuthEvent struct {
Expand All @@ -20,17 +20,16 @@ type userAuthEvent struct {
}

type userAuthResponse struct {
AccessToken string `json:"access_token,omitempty"`
RefreshToken string `json:"refresh_token,omitempty"`
ExpiresAt time.Time `json:"expires_at,omitempty"`
NewPasswordRequired bool
SessionID string `json:"session_id,omitempty"`
UserID string `json:"user_id,omitempty"`
AccessToken string `json:"access_token,omitempty"`
RefreshToken string `json:"refresh_token,omitempty"`
ExpiresAt time.Time `json:"expires_at,omitempty"`
SessionID string `json:"session_id,omitempty"`
UserID string `json:"user_id,omitempty"`
}

func (app application) generateAuthInput(e userAuthEvent, path string, secretHash string) *cidp.InitiateAuthInput {
input := &cidp.InitiateAuthInput{}
input.ClientId = aws.String(app.config.ClientPoolID)
func (app application) generateAuthInput(e userAuthEvent, path string, secretHash string) *cognitoidentityprovider.InitiateAuthInput {
input := &cognitoidentityprovider.InitiateAuthInput{}
input.ClientId = aws.String(app.Config.ClientPoolID)
if path == "/auth/login" {
input.AuthFlow = aws.String("USER_PASSWORD_AUTH")
input.AuthParameters = map[string]*string{
Expand All @@ -49,52 +48,52 @@ func (app application) generateAuthInput(e userAuthEvent, path string, secretHas
return input
}

func (app application) loginUser(e userAuthEvent, input *cidp.InitiateAuthInput) (userAuthResponse, error) {
func (app application) loginUser(e userAuthEvent, input *cognitoidentityprovider.InitiateAuthInput) (userAuthResponse, bool, error) {
loginUserResp := userAuthResponse{}
resp, err := app.config.idp.InitiateAuth(input)
var newPasswordRequired bool
resp, err := app.IDP.InitiateAuth(input)
if err != nil {
if aerr, ok := err.(awserr.Error); ok {
log.Printf("[ERROR] %v", aerr.Error())
log.Error(fmt.Sprintf("%v", aerr.Error()))
} else {
log.Printf("[ERROR] %v", err.Error())
log.Error(fmt.Sprintf("%v", err.Error()))
}
loginUserResp.NewPasswordRequired = false
return loginUserResp, err
newPasswordRequired = false
return loginUserResp, newPasswordRequired, err
}

if aws.StringValue(resp.ChallengeName) == "NEW_PASSWORD_REQUIRED" {
log.Printf("[INFO] New password required for %v", e.EmailAddress)
loginUserResp.NewPasswordRequired = true
log.Info(fmt.Sprintf("new password required for %v", e.EmailAddress))
newPasswordRequired = true
loginUserResp.SessionID = *resp.Session
loginUserResp.UserID = *resp.ChallengeParameters["USER_ID_FOR_SRP"]
return loginUserResp, nil
return loginUserResp, newPasswordRequired, nil
}
log.Printf("[INFO] Authenticated user %v successfully", e.EmailAddress)
log.Info(fmt.Sprintf("authenticated user %v successfully", e.EmailAddress))

now := time.Now()
loginUserResp.ExpiresAt = now.Add(time.Second * time.Duration(*resp.AuthenticationResult.ExpiresIn))
loginUserResp.AccessToken = *resp.AuthenticationResult.AccessToken
loginUserResp.RefreshToken = *resp.AuthenticationResult.RefreshToken
loginUserResp.NewPasswordRequired = false
return loginUserResp, nil
newPasswordRequired = false
return loginUserResp, newPasswordRequired, nil
}

func (app application) authLoginHandler(event events.APIGatewayV2HTTPRequest, headers map[string]string) (string, int, map[string]string) {
e := userAuthEvent{}
err := json.Unmarshal([]byte(event.Body), &e)
if err != nil {
log.Printf("[ERROR] %v", err)
log.Error(fmt.Sprintf("%v", err))
}

secretHash := util.GenerateSecretHash(app.config.ClientPoolSecret, e.EmailAddress, app.config.ClientPoolID)
secretHash := util.GenerateSecretHash(app.Config.ClientPoolSecret, e.EmailAddress, app.Config.ClientPoolID)
input := app.generateAuthInput(e, event.RawPath, secretHash)
loginUserResp, err := app.loginUser(e, input)
loginUserResp, newPasswordRequired, err := app.loginUser(e, input)
if err != nil {
message := fmt.Sprintf("Error authenticating user %v", e.EmailAddress)
statusCode := 400
return message, statusCode, headers

} else if loginUserResp.NewPasswordRequired {
} else if newPasswordRequired {
headers["X-Session-Id"] = loginUserResp.SessionID
message := fmt.Sprintf("User %v authorized successfully, password change required", e.EmailAddress)
statusCode := 200
Expand Down
62 changes: 34 additions & 28 deletions cmd/auth/login_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,45 +4,47 @@ import (
"testing"

"github.com/aws/aws-sdk-go/aws"
cidp "github.com/aws/aws-sdk-go/service/cognitoidentityprovider"
cidpif "github.com/aws/aws-sdk-go/service/cognitoidentityprovider/cognitoidentityprovideriface"
"github.com/aws/aws-sdk-go/service/cognitoidentityprovider"
"github.com/aws/aws-sdk-go/service/cognitoidentityprovider/cognitoidentityprovideriface"
)

type mockInitiateAuth struct {
cidpif.CognitoIdentityProviderAPI
Response *cidp.InitiateAuthOutput
cognitoidentityprovideriface.CognitoIdentityProviderAPI
Response *cognitoidentityprovider.InitiateAuthOutput
Error error
}

func (m mockInitiateAuth) InitiateAuth(*cidp.InitiateAuthInput) (*cidp.InitiateAuthOutput, error) {
func (m mockInitiateAuth) InitiateAuth(*cognitoidentityprovider.InitiateAuthInput) (*cognitoidentityprovider.InitiateAuthOutput, error) {
return m.Response, nil
}

func TestLoginUser(t *testing.T) {
t.Run("Successfully logged in user, user must change password", func(t *testing.T) {

idpMock := mockInitiateAuth{
Response: &cidp.InitiateAuthOutput{
Response: &cognitoidentityprovider.InitiateAuthOutput{
ChallengeName: aws.String("NEW_PASSWORD_REQUIRED"),
Session: aws.String("test"),
ChallengeParameters: map[string]*string{"USER_ID_FOR_SRP": aws.String("test")},
},
Error: nil,
}

app := application{config: configuration{
ClientPoolID: "test",
UserPoolID: "test",
idp: idpMock,
}}
app := application{
Config: configuration{
ClientPoolID: "test",
UserPoolID: "test",
},
IDP: idpMock,
}

event := userAuthEvent{
EmailAddress: "user@example.com",
Password: "example123$%^",
}

input := app.generateAuthInput(event, "/login/user", "exampleSecretHash")
_, err := app.loginUser(event, input)
_, _, err := app.loginUser(event, input)
if err != nil {
t.Fatal("User should have been logged in")
}
Expand All @@ -51,9 +53,9 @@ func TestLoginUser(t *testing.T) {
t.Run("Successfully logged in user", func(t *testing.T) {

idpMock := mockInitiateAuth{
Response: &cidp.InitiateAuthOutput{
Response: &cognitoidentityprovider.InitiateAuthOutput{
ChallengeName: nil,
AuthenticationResult: &cidp.AuthenticationResultType{
AuthenticationResult: &cognitoidentityprovider.AuthenticationResultType{
AccessToken: aws.String("test"),
RefreshToken: aws.String("test"),
ExpiresIn: aws.Int64(1),
Expand All @@ -62,19 +64,21 @@ func TestLoginUser(t *testing.T) {
Error: nil,
}

app := application{config: configuration{
ClientPoolID: "test",
UserPoolID: "test",
idp: idpMock,
}}
app := application{
Config: configuration{
ClientPoolID: "test",
UserPoolID: "test",
},
IDP: idpMock,
}

event := userAuthEvent{
EmailAddress: "user@example.com",
Password: "example123$%^",
}

input := app.generateAuthInput(event, "/login/user", "example_secret_hash")
_, err := app.loginUser(event, input)
_, _, err := app.loginUser(event, input)
if err != nil {
t.Fatal("User should have been logged in")
}
Expand All @@ -83,9 +87,9 @@ func TestLoginUser(t *testing.T) {
t.Run("Successfully refreshed user token", func(t *testing.T) {

idpMock := mockInitiateAuth{
Response: &cidp.InitiateAuthOutput{
Response: &cognitoidentityprovider.InitiateAuthOutput{
ChallengeName: nil,
AuthenticationResult: &cidp.AuthenticationResultType{
AuthenticationResult: &cognitoidentityprovider.AuthenticationResultType{
AccessToken: aws.String("test"),
RefreshToken: aws.String("test"),
ExpiresIn: aws.Int64(1),
Expand All @@ -94,19 +98,21 @@ func TestLoginUser(t *testing.T) {
Error: nil,
}

app := application{config: configuration{
ClientPoolID: "test",
UserPoolID: "test",
idp: idpMock,
}}
app := application{
Config: configuration{
ClientPoolID: "test",
UserPoolID: "test",
},
IDP: idpMock,
}

event := userAuthEvent{
EmailAddress: "user@example.com",
Password: "example123$%^",
}

input := app.generateAuthInput(event, "/refresh/token", "example_secret_hash")
_, err := app.loginUser(event, input)
_, _, err := app.loginUser(event, input)
if err != nil {
t.Fatal("User should have been logged in")
}
Expand Down
30 changes: 20 additions & 10 deletions cmd/auth/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,56 +2,66 @@ package main

import (
"fmt"
"log"
"os"

"github.com/aws/aws-lambda-go/events"
"github.com/aws/aws-lambda-go/lambda"
"github.com/aws/aws-sdk-go/aws/session"
cidp "github.com/aws/aws-sdk-go/service/cognitoidentityprovider"
cidpif "github.com/aws/aws-sdk-go/service/cognitoidentityprovider/cognitoidentityprovideriface"
"github.com/aws/aws-sdk-go/service/cognitoidentityprovider"
"github.com/aws/aws-sdk-go/service/cognitoidentityprovider/cognitoidentityprovideriface"
"github.com/aws/aws-sdk-go/service/dynamodb"
"github.com/aws/aws-sdk-go/service/dynamodb/dynamodbiface"
"github.com/seanturner026/serverless-release-dashboard/internal/util"
log "github.com/sirupsen/logrus"
)

type application struct {
config configuration
Config configuration
DB dynamodbiface.DynamoDBAPI
IDP cognitoidentityprovideriface.CognitoIdentityProviderAPI
}

type configuration struct {
TableName string
ClientPoolID string
UserPoolID string
ClientPoolSecret string
idp cidpif.CognitoIdentityProviderAPI
}

func (app application) handler(event events.APIGatewayV2HTTPRequest) (events.APIGatewayV2HTTPResponse, error) {
headers := map[string]string{"Content-Type": "application/json"}

if event.RawPath == "/auth/login" || event.RawPath == "/auth/refresh/token" {
log.Printf("[INFO] handling request on %v", event.RawPath)
log.Info(fmt.Sprintf("handling request on %v", event.RawPath))
message, statusCode, headers := app.authLoginHandler(event, headers)
return util.GenerateResponseBody(message, statusCode, nil, headers, []string{}), nil

} else if event.RawPath == "/auth/reset/password" {
log.Printf("[INFO] handling request on %v", event.RawPath)
log.Info(fmt.Sprintf("handling request on %v", event.RawPath))
message, statusCode, headers := app.authResetPasswordHandler(event, headers)
return util.GenerateResponseBody(message, statusCode, nil, headers, []string{}), nil

} else {
log.Printf("[ERROR] path %v does not exist", event.RawPath)
log.Error(fmt.Sprintf("path %v does not exist", event.RawPath))
return util.GenerateResponseBody(fmt.Sprintf("Path does not exist %v", event.RawPath), 404, nil, headers, []string{}), nil
}
}

func main() {
log.SetFormatter(&log.JSONFormatter{})

config := configuration{
TableName: os.Getenv("TABLE_NAME"),
ClientPoolID: os.Getenv("CLIENT_POOL_ID"),
UserPoolID: os.Getenv("USER_POOL_ID"),
ClientPoolSecret: os.Getenv("CLIENT_POOL_SECRET"),
idp: cidp.New(session.Must(session.NewSession())),
}

app := application{config: config}
app := application{
Config: config,
DB: dynamodb.New(session.Must(session.NewSession())),
IDP: cognitoidentityprovider.New(session.Must(session.NewSession())),
}

lambda.Start(app.handler)
}
Loading

0 comments on commit 1348439

Please sign in to comment.