Allow operations to opt out of auth middleware

This patch allows operations to individually specify whether they want
to use or opt out of auth middleware, via an additional `auth` argument
under the actions/queries sections. If left unspecified it is assumed to
be true.

This is addressed by removing the current global auth middleware
registration, and adding it instead to individual operation route
specification.
This commit is contained in:
Saatvik Shah 2021-06-24 22:06:57 +02:00
parent 83b00d03ce
commit a85b408b60
11 changed files with 108 additions and 50 deletions

View File

@ -11,12 +11,8 @@ import {= importIdentifier =} from '{= importPath =}'
const router = express.Router()
{=# isAuthEnabled =}
router.use(auth)
{=/ isAuthEnabled =}
{=# operationRoutes =}
router.post('{= routePath =}', {= importIdentifier =})
router.post('{= routePath =}', {=# isAuthEnabled =} auth, {=/ isAuthEnabled =} {= importIdentifier =})
{=/ operationRoutes =}
export default router

View File

@ -6,7 +6,7 @@ where
import Data.Aeson (object, (.=))
import qualified Data.Aeson as Aeson
import Data.Maybe (fromJust, isJust)
import Data.Maybe (fromJust)
import Generator.FileDraft (FileDraft)
import qualified Generator.ServerGenerator.Common as C
import Generator.ServerGenerator.OperationsG (operationFileInSrcDir)
@ -97,15 +97,19 @@ genOperationsRouter wasp = C.makeTemplateFD tmplFile dstFile (Just tmplData)
tmplData =
object
[ "operationRoutes" .= map makeOperationRoute operations,
"isAuthEnabled" .= (isJust $ getAuth wasp)
"isAuthEnabled" .= (or $ map authEnabled operations)
]
makeOperationRoute operation =
let operationName = Wasp.Operation.getName operation
in object
[ "importIdentifier" .= operationName,
"importPath" .= ("./" ++ SP.toFilePath (SP.relFileToPosix' $ operationRouteFileInOperationsRoutesDir operation)),
"routePath" .= ("/" ++ operationRouteInOperationsRouter operation)
"routePath" .= ("/" ++ operationRouteInOperationsRouter operation),
"isAuthEnabled" .= (authEnabled operation)
]
authEnabled :: Wasp.Operation.Operation -> Bool
authEnabled op = (Wasp.Operation.getAuth op /= Just False)
operationRouteInOperationsRouter :: Wasp.Operation.Operation -> String
operationRouteInOperationsRouter = U.camelToKebabCase . Wasp.Operation.getName

View File

@ -19,5 +19,6 @@ action = do
{ Action._name = name,
Action._jsFunction =
fromMaybe (error "Action js function is missing.") (Operation.getJsFunctionFromProps props),
Action._entities = Operation.getEntitiesFromProps props
Action._entities = Operation.getEntitiesFromProps props,
Action._auth = Operation.getAuthEnabledFromProps props
}

View File

@ -3,6 +3,7 @@ module Parser.Operation
entitiesPropParser,
getJsFunctionFromProps,
getEntitiesFromProps,
getAuthEnabledFromProps,
properties,
-- FOR TESTS:
Property (..),
@ -20,6 +21,7 @@ import qualified Wasp.JsImport
data Property
= JsFunction !Wasp.JsImport.JsImport
| Entities ![String]
| AuthEnabled !Bool
deriving (Show, Eq)
properties :: Parser [Property]
@ -27,6 +29,7 @@ properties =
L.commaSep1 $
jsFunctionPropParser
<|> entitiesPropParser
<|> authEnabledPropParser
jsFunctionPropParser :: Parser Property
jsFunctionPropParser = JsFunction <$> C.waspProperty "fn" Parser.JsImport.jsImport
@ -39,3 +42,9 @@ entitiesPropParser = Entities <$> C.waspProperty "entities" (C.waspList L.identi
getEntitiesFromProps :: [Property] -> Maybe [String]
getEntitiesFromProps ps = listToMaybe [es | Entities es <- ps]
authEnabledPropParser :: Parser Property
authEnabledPropParser = AuthEnabled <$> C.waspProperty "auth" L.bool
getAuthEnabledFromProps :: [Property] -> Maybe Bool
getAuthEnabledFromProps ps = listToMaybe [aE | AuthEnabled aE <- ps]

View File

@ -19,5 +19,6 @@ query = do
{ Query._name = name,
Query._jsFunction =
fromMaybe (error "Query js function is missing.") (Operation.getJsFunctionFromProps props),
Query._entities = Operation.getEntitiesFromProps props
Query._entities = Operation.getEntitiesFromProps props,
Query._auth = Operation.getAuthEnabledFromProps props
}

View File

@ -11,7 +11,8 @@ import Wasp.JsImport (JsImport)
data Action = Action
{ _name :: !String,
_jsFunction :: !JsImport,
_entities :: !(Maybe [String])
_entities :: !(Maybe [String]),
_auth :: !(Maybe Bool)
}
deriving (Show, Eq)
@ -20,5 +21,6 @@ instance ToJSON Action where
object
[ "name" .= _name action,
"jsFunction" .= _jsFunction action,
"entities" .= _entities action
"entities" .= _entities action,
"auth" .= _auth action
]

View File

@ -3,6 +3,7 @@ module Wasp.Operation
getName,
getJsFn,
getEntities,
getAuth
)
where
@ -30,3 +31,7 @@ getJsFn (ActionOp action) = Action._jsFunction action
getEntities :: Operation -> Maybe [String]
getEntities (QueryOp query) = Query._entities query
getEntities (ActionOp action) = Action._entities action
getAuth :: Operation -> Maybe Bool
getAuth (QueryOp query) = Query._auth query
getAuth (ActionOp action) = Action._auth action

View File

@ -11,7 +11,8 @@ import Wasp.JsImport (JsImport)
data Query = Query
{ _name :: !String,
_jsFunction :: !JsImport,
_entities :: !(Maybe [String])
_entities :: !(Maybe [String]),
_auth :: !(Maybe Bool)
}
deriving (Show, Eq)
@ -20,5 +21,6 @@ instance ToJSON Query where
object
[ "name" .= _name query,
"jsFunction" .= _jsFunction query,
"entities" .= _entities query
"entities" .= _entities query,
"auth" .= _auth query
]

View File

@ -1,6 +1,7 @@
module Parser.ActionTest where
import Data.Either (isLeft)
import Data.Char (toLower)
import Parser.Action (action)
import Parser.Common (runWaspParser)
import qualified Path.Posix as PPosix
@ -19,12 +20,23 @@ spec_parseAction =
describe "Parsing action declaration" $ do
let parseAction = runWaspParser action
it "When given a valid action declaration, returns correct AST" $ do
let testActionName = "myAction"
testActionJsFunctionName = "myJsAction"
testActionJsFunctionFrom = SP.fromPathRelFileP [PPosix.relfile|some/path|]
let testAction =
Wasp.Action.Action
it "When given a valid action declaration, returns correct AST (no auth)" $ do
let testAction = genActionAST Nothing
let testActionInput = genActionInput Nothing
parseAction testActionInput `shouldBe` Right testAction
it "When given a valid action declaration, returns correct AST (auth = true)" $ do
let testAction = genActionAST (Just True)
let testActionInput = genActionInput (Just True)
parseAction testActionInput `shouldBe` Right testAction
it "When given a valid action declaration, returns correct AST (auth = false)" $ do
let testAction = genActionAST (Just False)
let testActionInput = genActionInput (Just False)
parseAction testActionInput `shouldBe` Right testAction
it "When given action wasp declaration without 'fn' property, should return Left" $ do
isLeft (parseAction "action myAction { }") `shouldBe` True
where
genActionAST :: Maybe Bool -> Wasp.Action.Action
genActionAST aApplyAuth = Wasp.Action.Action
{ Wasp.Action._name = testActionName,
Wasp.Action._jsFunction =
Wasp.JsImport.JsImport
@ -32,15 +44,21 @@ spec_parseAction =
Wasp.JsImport._namedImports = [testActionJsFunctionName],
Wasp.JsImport._from = testActionJsFunctionFrom
},
Wasp.Action._entities = Nothing
Wasp.Action._entities = Nothing,
Wasp.Action._auth = aApplyAuth
}
parseAction
( "action " ++ testActionName ++ " {\n"
++ " fn: import { "
++ testActionJsFunctionName
++ " } from \"@ext/some/path\"\n"
++ "}"
)
`shouldBe` Right testAction
it "When given action wasp declaration without 'fn' property, should return Left" $ do
isLeft (parseAction "action myAction { }") `shouldBe` True
genActionInput :: Maybe Bool -> String
genActionInput aApplyAuth = (
"action " ++ testActionName ++ " {\n"
++ " fn: import { "
++ testActionJsFunctionName
++ " } from \"@ext/some/path\""
++ authStr aApplyAuth
++ "}"
)
authStr :: Maybe Bool -> String
authStr (Just useAuth) = ",\n auth: " ++ map toLower (show useAuth) ++ "\n"
authStr _ = "\n"
testActionJsFunctionFrom = SP.fromPathRelFileP [PPosix.relfile|some/path|]
testActionJsFunctionName = "myJsAction"
testActionName = "myAction"

View File

@ -108,7 +108,8 @@ spec_parseWasp =
Wasp.JsImport._namedImports = ["myJsQuery"],
Wasp.JsImport._from = SP.fromPathRelFileP [PPosix.relfile|some/path|]
},
Wasp.Query._entities = Nothing
Wasp.Query._entities = Nothing,
Wasp.Query._auth = Nothing
},
WaspElementNpmDependencies $
Wasp.NpmDependencies.NpmDependencies

View File

@ -8,18 +8,43 @@ import qualified StrongPath as SP
import Test.Tasty.Hspec
import qualified Wasp.JsImport
import qualified Wasp.Query
import Data.Char (toLower)
spec_parseQuery :: Spec
spec_parseQuery =
describe "Parsing query declaration" $ do
let parseQuery = runWaspParser query
it "When given a valid query declaration, returns correct AST" $ do
let testQueryName = "myQuery"
testQueryJsFunctionName = "myJsQuery"
testQueryJsFunctionFrom = SP.fromPathRelFileP [PPosix.relfile|some/path|]
let testQuery =
Wasp.Query.Query
it "When given a valid query declaration, returns correct AST(without auth)" $ do
let testQuery = genQueryAST Nothing
let testQueryInput = genQueryInput Nothing
parseQuery testQueryInput `shouldBe` Right testQuery
it "When given query wasp declaration without 'fn' property, should return Left" $ do
isLeft (parseQuery "query myQuery { }") `shouldBe` True
it "When given a valid query declaration, returns correct AST(with auth=true)" $ do
let testQuery = genQueryAST (Just True)
let testQueryInput = genQueryInput (Just True)
parseQuery testQueryInput `shouldBe` Right testQuery
it "When given a valid query declaration, returns correct AST(with auth=false)" $ do
let testQuery = genQueryAST (Just False)
let testQueryInput = genQueryInput (Just False)
parseQuery testQueryInput `shouldBe` Right testQuery
where
genQueryInput :: Maybe Bool -> String
genQueryInput qApplyAuth = (
"query " ++ testQueryName ++ " {\n"
++ " fn: import { "
++ testQueryJsFunctionName
++ " } from \"@ext/some/path\",\n"
++ " entities: [Task, Project]"
++ authStr qApplyAuth
++ "}"
)
authStr :: Maybe Bool -> String
authStr (Just useAuth) = ",\n auth: " ++ map toLower (show useAuth) ++ "\n"
authStr _ = "\n"
genQueryAST :: Maybe Bool -> Wasp.Query.Query
genQueryAST qApplyAuth = Wasp.Query.Query
{ Wasp.Query._name = testQueryName,
Wasp.Query._jsFunction =
Wasp.JsImport.JsImport
@ -27,16 +52,10 @@ spec_parseQuery =
Wasp.JsImport._namedImports = [testQueryJsFunctionName],
Wasp.JsImport._from = testQueryJsFunctionFrom
},
Wasp.Query._entities = Just ["Task", "Project"]
Wasp.Query._entities = Just ["Task", "Project"],
Wasp.Query._auth = qApplyAuth
}
parseQuery
( "query " ++ testQueryName ++ " {\n"
++ " fn: import { "
++ testQueryJsFunctionName
++ " } from \"@ext/some/path\",\n"
++ " entities: [Task, Project]\n"
++ "}"
)
`shouldBe` Right testQuery
it "When given query wasp declaration without 'fn' property, should return Left" $ do
isLeft (parseQuery "query myQuery { }") `shouldBe` True
testQueryName = "myQuery"
testQueryJsFunctionName = "myJsQuery"
testQueryJsFunctionFrom = SP.fromPathRelFileP [PPosix.relfile|some/path|]