Skip to content
This repository was archived by the owner on Feb 6, 2024. It is now read-only.

Commit 5dab851

Browse files
authored
Merge pull request #90 from deckgo/nm-fix-fuid
handler: unique firebase uid and fix del/put
2 parents 1c56dfc + 6307a02 commit 5dab851

File tree

7 files changed

+84
-35
lines changed

7 files changed

+84
-35
lines changed

infra/default.nix

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,8 @@ rec
6060
aws dynamodb create-table \
6161
--table-name Users \
6262
--attribute-definitions \
63-
AttributeName=UserId,AttributeType=S \
64-
--key-schema AttributeName=UserId,KeyType=HASH \
63+
AttributeName=UserFirebaseId,AttributeType=S \
64+
--key-schema AttributeName=UserFirebaseId,KeyType=HASH \
6565
--endpoint-url http://127.0.0.1:8000 \
6666
--provisioned-throughput ReadCapacityUnits=1,WriteCapacityUnits=1 \
6767
> /dev/null

infra/dynamo.tf

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
resource "aws_dynamodb_table" "deckdeckgo-test-dynamodb-table-users" {
22
name = "Users"
33
billing_mode = "PAY_PER_REQUEST"
4-
hash_key = "UserId"
4+
hash_key = "UserFirebaseId"
55

66
attribute {
7-
name = "UserId"
7+
name = "UserFirebaseId"
88
type = "S"
99
}
1010

infra/handler/app/Handler.hs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
{-# LANGUAGE OverloadedStrings #-}
22

3-
import UnliftIO
43
import Control.Lens
54
import Servant.Auth.Firebase (ProjectId(..))
6-
import qualified Network.AWS as Aws
5+
import UnliftIO
76
import qualified DeckGo.Handler
7+
import qualified Network.AWS as Aws
8+
import qualified Network.HTTP.Types as HTTP
9+
import qualified Network.Wai as Wai
810
import qualified Network.Wai.Handler.Lambda as Lambda
911
import qualified Network.Wai.Middleware.Cors as Cors
1012

@@ -21,4 +23,18 @@ main = do
2123
-- TODO: from env
2224
let projectId = ProjectId "deckdeckgo-studio-beta"
2325

24-
Lambda.run $ Cors.simpleCors $ DeckGo.Handler.application (env ^. Aws.envManager) projectId env
26+
Lambda.run $ cors $ DeckGo.Handler.application (env ^. Aws.envManager) projectId env
27+
28+
cors :: Wai.Middleware
29+
cors = Cors.cors $
30+
const $
31+
Just Cors.simpleCorsResourcePolicy { Cors.corsMethods = methods }
32+
33+
methods :: [HTTP.Method]
34+
methods =
35+
[ "GET"
36+
, "HEAD"
37+
, "POST"
38+
, "DELETE"
39+
, "PUT"
40+
]

infra/handler/app/Test.hs

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
{-# LANGUAGE LambdaCase #-}
33

44
import Network.HTTP.Client (newManager, defaultManagerSettings)
5+
import Network.HTTP.Types as HTTP
56
import Servant.API
67
import Servant.Client
78
import DeckGo.Handler
@@ -30,7 +31,8 @@ main = do
3031
Right [] -> pure ()
3132
Right decks -> error $ "Expected 0 decks, got: " <> show decks
3233

33-
let someUserId = UserId "foo"
34+
let someFirebaseId = FirebaseId "the-uid" -- from ./token
35+
let someUserId = UserId someFirebaseId
3436

3537
let someDeck = Deck { deckSlides = [] , deckDeckname = Deckname "foo", deckOwnerId = someUserId }
3638

@@ -100,9 +102,30 @@ main = do
100102
if decks == [] then pure () else (error $ "Expected no decks, got: " <> show decks)
101103

102104

105+
let someUser = User { userFirebaseId = someFirebaseId, userAnonymous = False }
106+
107+
runClientM (usersPost' b someUser) clientEnv >>= \case
108+
Left err -> error $ "Expected user, got error: " <> show err
109+
Right (Item userId user) ->
110+
if user == someUser && userId == someUserId then pure () else (error $ "Expected same user, got: " <> show user)
111+
112+
runClientM (usersPost' b someUser) clientEnv >>= \case
113+
Left (FailureResponse resp) ->
114+
if HTTP.statusCode (responseStatusCode resp) == 409 then pure () else
115+
error $ "Got unexpecte response: " <> show resp
116+
Left err -> error $ "Expected 409, got error: " <> show err
117+
Right item -> error $ "Expected failure, got success: " <> show item
118+
119+
120+
-- TODO: test that creating user with token that has different user as sub
121+
-- fails
122+
123+
124+
125+
103126
usersGet' :: ClientM [Item UserId User]
104127
_usersGetUserId' :: UserId -> ClientM (Item UserId User)
105-
_usersPost' :: T.Text -> User -> ClientM (Item UserId User)
128+
usersPost' :: T.Text -> User -> ClientM (Item UserId User)
106129
_usersPut' :: T.Text -> UserId -> User -> ClientM (Item UserId User)
107130
_usersDelete' :: T.Text -> UserId -> ClientM ()
108131

@@ -120,7 +143,7 @@ slidesDelete' :: SlideId -> ClientM ()
120143
((
121144
usersGet' :<|>
122145
_usersGetUserId' :<|>
123-
_usersPost' :<|>
146+
usersPost' :<|>
124147
_usersPut' :<|>
125148
_usersDelete'
126149
) :<|>

infra/handler/package.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,15 @@ dependencies:
1313
- filepath
1414
- firebase-login
1515
- http-client
16+
- http-types
1617
- lens
1718
- mtl
1819
- random
1920
- servant
20-
- swagger2
2121
- servant-server
2222
- servant-swagger
2323
- servant-swagger-ui
24+
- swagger2
2425
- text
2526
- unliftio
2627
- unordered-containers

infra/handler/src/DeckGo/Handler.hs

Lines changed: 31 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,11 @@
1717
module DeckGo.Handler where
1818

1919
-- TODO: double check what is returned on 200 from DynamoDB
20-
-- TODO: check user is in DB
2120
-- TODO: check permissions
2221
-- TODO: created_at, updated_at
2322
-- TODO: TTL on anonymous users
23+
-- TODO: enforce uniqueness on deck_name (per user)
24+
-- TODO: improve swagger description
2425

2526
import Control.Lens hiding ((.=))
2627
import Control.Monad
@@ -90,11 +91,11 @@ newtype Username = Username { unUsername :: T.Text }
9091
deriving newtype (Aeson.FromJSON, Aeson.ToJSON)
9192

9293
data User = User
93-
{ userFirebaseId :: FirebaseId -- TODO: enforce uniqueness
94+
{ userFirebaseId :: FirebaseId
9495
, userAnonymous :: Bool
9596
} deriving (Show, Eq)
9697

97-
newtype UserId = UserId { unUserId :: T.Text }
98+
newtype UserId = UserId { unUserId :: FirebaseId }
9899
deriving newtype
99100
( Aeson.FromJSON
100101
, Aeson.ToJSON
@@ -171,7 +172,7 @@ newtype Deckname = Deckname { unDeckname :: T.Text }
171172

172173
data Deck = Deck
173174
{ deckSlides :: [SlideId]
174-
, deckDeckname :: Deckname -- TODO: enforce uniqueness
175+
, deckDeckname :: Deckname
175176
, deckOwnerId :: UserId
176177
} deriving (Show, Eq)
177178

@@ -323,7 +324,7 @@ usersGet env = do
323324
usersGetUserId :: Aws.Env -> UserId -> Servant.Handler (Item UserId User)
324325
usersGetUserId env userId = do
325326
res <- runAWS env $ Aws.send $ DynamoDB.getItem "Users" &
326-
DynamoDB.giKey .~ HMS.singleton "UserId" (userIdToAttributeValue userId)
327+
DynamoDB.giKey .~ HMS.singleton "UserFirebaseId" (userIdToAttributeValue userId)
327328
case res of
328329
Right getItemResponse -> do
329330
case getItemResponse ^. DynamoDB.girsResponseStatus of
@@ -347,18 +348,26 @@ usersGetUserId env userId = do
347348
Servant.throwError Servant.err500
348349

349350
usersPost :: Aws.Env -> Firebase.UserId -> User -> Servant.Handler (Item UserId User)
350-
usersPost env _uid user = do
351+
usersPost env fuid user = do
351352

352-
userId <- liftIO $ UserId <$> newId
353+
let userId = UserId (userFirebaseId user)
354+
355+
when (Firebase.unUserId fuid /= unFirebaseId (userFirebaseId user)) $ do
356+
Servant.throwError Servant.err403
353357

354358
res <- runAWS env $ Aws.send $ DynamoDB.putItem "Users" &
355-
DynamoDB.piItem .~ userToItem userId user
359+
DynamoDB.piItem .~ userToItem userId user &
360+
DynamoDB.piConditionExpression .~ Just "attribute_not_exists(UserFirebaseId)"
356361

357362
case res of
358363
Right {} -> pure ()
359-
Left e -> do
360-
liftIO $ print e
361-
Servant.throwError Servant.err500
364+
Left e -> case e ^? DynamoDB._ConditionalCheckFailedException of
365+
Just _e -> do
366+
u <- usersGetUserId env userId
367+
Servant.throwError Servant.err409 { Servant.errBody = Aeson.encode u }
368+
Nothing -> do
369+
liftIO $ print e
370+
Servant.throwError Servant.err500
362371

363372
pure $ Item userId user
364373

@@ -367,7 +376,7 @@ usersPut env _ userId user = do
367376

368377
res <- runAWS env $ Aws.send $ DynamoDB.updateItem "Users" &
369378
DynamoDB.uiUpdateExpression .~
370-
Just "SET UserDecks = :s, UserUsername = :n, UserFirebaseId = :i" &
379+
Just "SET UserDecks = :s, UserUsername = :n" &
371380
DynamoDB.uiExpressionAttributeValues .~ userToItem' user &
372381
DynamoDB.uiReturnValues .~ Just DynamoDB.UpdatedNew &
373382
DynamoDB.uiKey .~ HMS.singleton "UserId"
@@ -385,7 +394,7 @@ usersDelete :: Aws.Env -> Firebase.UserId -> UserId -> Servant.Handler ()
385394
usersDelete env _ userId = do
386395

387396
res <- runAWS env $ Aws.send $ DynamoDB.deleteItem "Users" &
388-
DynamoDB.diKey .~ HMS.singleton "UserId"
397+
DynamoDB.diKey .~ HMS.singleton "UserFirebaseId"
389398
(userIdToAttributeValue userId)
390399

391400
case res of
@@ -588,31 +597,29 @@ slidesDelete env slideId = do
588597
-- USERS
589598

590599
userToItem :: UserId -> User -> HMS.HashMap T.Text DynamoDB.AttributeValue
591-
userToItem userId User{userFirebaseId, userAnonymous} =
592-
HMS.singleton "UserId" (userIdToAttributeValue userId) <>
593-
HMS.singleton "UserFirebaseId" (userFirebaseIdToAttributeValue userFirebaseId) <>
600+
userToItem userId User{userAnonymous} =
601+
HMS.singleton "UserFirebaseId" (userIdToAttributeValue userId) <>
594602
HMS.singleton "UserAnonymous" (userAnonymousToAttributeValue userAnonymous)
595603

596604
userToItem' :: User -> HMS.HashMap T.Text DynamoDB.AttributeValue
597-
userToItem' User{userFirebaseId, userAnonymous} =
598-
HMS.singleton ":i" (userFirebaseIdToAttributeValue userFirebaseId) <>
605+
userToItem' User{userAnonymous} =
599606
HMS.singleton ":a" (userAnonymousToAttributeValue userAnonymous)
600607

601608
itemToUser :: HMS.HashMap T.Text DynamoDB.AttributeValue -> Maybe (Item UserId User)
602609
itemToUser item = do
603-
userId <- HMS.lookup "UserId" item >>= userIdFromAttributeValue
604-
userFirebaseId <- HMS.lookup "UserFirebaseId" item >>= userFirebaseIdFromAttributeValue
610+
userId <- HMS.lookup "UserFirebaseId" item >>= userIdFromAttributeValue
611+
let userFirebaseId = unUserId userId
605612
userAnonymous <- HMS.lookup "UserAnonymous" item >>= userAnonymousFromAttributeValue
606613
pure $ Item userId User{..}
607614

608615
-- USER ATTRIBUTES
609616

610617
userIdToAttributeValue :: UserId -> DynamoDB.AttributeValue
611-
userIdToAttributeValue (UserId userId) =
618+
userIdToAttributeValue (UserId (FirebaseId userId)) =
612619
DynamoDB.attributeValue & DynamoDB.avS .~ Just userId
613620

614621
userIdFromAttributeValue :: DynamoDB.AttributeValue -> Maybe UserId
615-
userIdFromAttributeValue attr = UserId <$> attr ^. DynamoDB.avS
622+
userIdFromAttributeValue attr = (UserId . FirebaseId) <$> attr ^. DynamoDB.avS
616623

617624
userNameToAttributeValue :: Username -> DynamoDB.AttributeValue
618625
userNameToAttributeValue (Username username) =
@@ -693,11 +700,11 @@ deckSlidesFromAttributeValue attr =
693700
traverse slideIdFromAttributeValue (attr ^. DynamoDB.avL)
694701

695702
deckOwnerIdToAttributeValue :: UserId -> DynamoDB.AttributeValue
696-
deckOwnerIdToAttributeValue (UserId deckOwnerId) =
703+
deckOwnerIdToAttributeValue (UserId (FirebaseId deckOwnerId)) =
697704
DynamoDB.attributeValue & DynamoDB.avS .~ Just deckOwnerId
698705

699706
deckOwnerIdFromAttributeValue :: DynamoDB.AttributeValue -> Maybe UserId
700-
deckOwnerIdFromAttributeValue attr = UserId <$> attr ^. DynamoDB.avS
707+
deckOwnerIdFromAttributeValue attr = (UserId . FirebaseId) <$> attr ^. DynamoDB.avS
701708

702709
-- SLIDES
703710

infra/lambda.tf

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,13 @@ data "aws_iam_policy_document" "policy_for_lambda" {
5252
actions = [
5353
"dynamodb:BatchGetItem",
5454
"dynamodb:GetItem",
55+
"dynamodb:UpdateItem",
5556
"dynamodb:Query",
5657
"dynamodb:Scan",
5758
"dynamodb:BatchWriteItem",
5859
"dynamodb:PutItem",
5960
"dynamodb:UpdateItem",
61+
"dynamodb:DeleteItem",
6062
]
6163

6264
resources = [

0 commit comments

Comments
 (0)