Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Authentication and Authorization #1

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions Application/Fixtures.sql
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,25 @@ SET row_security = off;

SET SESSION AUTHORIZATION DEFAULT;

ALTER TABLE public.posts DISABLE TRIGGER ALL;
ALTER TABLE public.users DISABLE TRIGGER ALL;

INSERT INTO public.users (id, email, password_hash, locked_at, failed_login_attempts) VALUES ('9f563e7a-eaf6-4629-920c-165f6dc03562', '[email protected]', 'sha256|17|6ok5wosHG4fndz8PVOqQmQ==|bL11xiCmLx5ZePdf3MoPgb6keGgHacZEX3e4V2qOJx8=', NULL, 0);
INSERT INTO public.users (id, email, password_hash, locked_at, failed_login_attempts) VALUES ('2d78bb96-342c-4413-8d88-a8e321e08f53', '[email protected]', 'sha256|17|9H9HhisEo6NYc6naoENeLw==|yHolD2GGPKnqRTGRH4LAb97IKXnQ2sAjQD8Etm73kTU=', NULL, 0);
INSERT INTO public.users (id, email, password_hash, locked_at, failed_login_attempts) VALUES ('9c3253cc-8bfe-44d5-a2bd-71d68541fbc9', '[email protected]', 'sha256|17|PHrQu9flAGbENBwm+mZyqA==|XVlBvsI9eotjPii+OEmtUmDYli2zKhgoZKBW3GKZXn4=', NULL, 0);


INSERT INTO public.posts (id, title, body, created_at) VALUES ('4ef48ade-e7f0-4afb-b4d9-5d4d1f7b9b86', 'Hello World!', 'Lorem ipsum *dolor sit amet*, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam. **This is the IHP Blog Demo App**.
ALTER TABLE public.users ENABLE TRIGGER ALL;


ALTER TABLE public.posts DISABLE TRIGGER ALL;

Lorem ipsum *dolor sit amet*, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam', '2020-06-07 13:10:04.340407+02');


ALTER TABLE public.posts ENABLE TRIGGER ALL;


ALTER TABLE public.comments DISABLE TRIGGER ALL;

INSERT INTO public.comments (id, post_id, author, body, created_at) VALUES ('1de9007a-b690-4f2d-a76e-d08cc7d8e308', '4ef48ade-e7f0-4afb-b4d9-5d4d1f7b9b86', 'Marc', 'This is the first comment!', '2020-06-07 13:11:22.07897+02');
INSERT INTO public.comments (id, post_id, author, body, created_at) VALUES ('7386928d-a6fc-4f20-ada9-333c32345453', '4ef48ade-e7f0-4afb-b4d9-5d4d1f7b9b86', 'Marc', 'This is a second comment!', '2020-06-07 13:11:30.718887+02');


ALTER TABLE public.comments ENABLE TRIGGER ALL;
Expand Down
6 changes: 4 additions & 2 deletions Application/Helper/Controller.hs
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,7 @@ module Application.Helper.Controller (

-- Here you can add functions which are available in all your controllers

-- To use the built in login:
-- import IHP.LoginSupport.Helper.Controller
import IHP.LoginSupport.Helper.Controller
import Generated.Types

type instance CurrentUserRecord = User
3 changes: 1 addition & 2 deletions Application/Helper/View.hs
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,4 @@ module Application.Helper.View (

-- Here you can add functions which are available in all your views

-- To use the built in login:
-- import IHP.LoginSupport.Helper.View
import IHP.LoginSupport.Helper.View
15 changes: 13 additions & 2 deletions Application/Schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,24 @@ CREATE TABLE posts (
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
title TEXT NOT NULL,
body TEXT NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() NOT NULL
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() NOT NULL,
user_id UUID NOT NULL
);
CREATE TABLE comments (
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
post_id UUID NOT NULL,
author TEXT NOT NULL,
body TEXT NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() NOT NULL
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() NOT NULL,
user_id UUID NOT NULL
);
CREATE TABLE users (
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
email TEXT NOT NULL,
password_hash TEXT NOT NULL,
locked_at TIMESTAMP WITH TIME ZONE DEFAULT NULL,
failed_login_attempts INT DEFAULT 0 NOT NULL
);
ALTER TABLE comments ADD CONSTRAINT comments_ref_user_id FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE NO ACTION;
ALTER TABLE posts ADD CONSTRAINT posts_ref_user_id FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE NO ACTION;
ALTER TABLE comments ADD CONSTRAINT comments_ref_post_id FOREIGN KEY (post_id) REFERENCES posts (id) ON DELETE CASCADE;
5 changes: 5 additions & 0 deletions Web/Controller/Comments.hs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import Web.View.Comments.Index
import Web.View.Comments.New
import Web.View.Comments.Edit
import Web.View.Comments.Show
import IHP.AuthSupport.Authorization
import IHP.LoginSupport.Helper.Controller

instance Controller CommentsController where
action CommentsAction = do
Expand All @@ -23,6 +25,7 @@ instance Controller CommentsController where

action EditCommentAction { commentId } = do
comment <- fetch commentId
accessDeniedUnless (get #userId comment == currentUserId)
render EditView { .. }

action UpdateCommentAction { commentId } = do
Expand All @@ -40,6 +43,7 @@ instance Controller CommentsController where
let comment = newRecord @Comment
comment
|> buildComment
|> set #userId (currentUserId)
|> ifValid \case
Left comment -> do
post <- fetch (get #postId comment)
Expand All @@ -51,6 +55,7 @@ instance Controller CommentsController where

action DeleteCommentAction { commentId } = do
comment <- fetch commentId
accessDeniedUnless (get #userId comment == currentUserId)
deleteRecord comment
setSuccessMessage "Comment deleted"
redirectTo CommentsAction
Expand Down
7 changes: 7 additions & 0 deletions Web/Controller/Posts.hs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,12 @@ import Web.View.Posts.New
import Web.View.Posts.Edit
import Web.View.Posts.Show
import qualified Text.MMark as MMark
import IHP.LoginSupport.Helper.Controller
import IHP.AuthSupport.Authorization

instance Controller PostsController where
beforeAction = ensureIsUser

action PostsAction = do
posts <- query @Post
|> orderByDesc #createdAt
Expand All @@ -26,6 +30,7 @@ instance Controller PostsController where

action EditPostAction { postId } = do
post <- fetch postId
accessDeniedUnless (get #userId post == currentUserId)
render EditView { .. }

action UpdatePostAction { postId } = do
Expand All @@ -43,6 +48,7 @@ instance Controller PostsController where
let post = newRecord @Post
post
|> buildPost
|> set #userId (currentUserId)
|> ifValid \case
Left post -> render NewView { .. }
Right post -> do
Expand All @@ -52,6 +58,7 @@ instance Controller PostsController where

action DeletePostAction { postId } = do
post <- fetch postId
accessDeniedUnless (get #userId post == currentUserId)
deleteRecord post
setSuccessMessage "Post deleted"
redirectTo PostsAction
Expand Down
9 changes: 8 additions & 1 deletion Web/FrontController.hs
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,25 @@ import IHP.RouterPrelude
import IHP.ControllerSupport
import Generated.Types
import Web.Types
import IHP.LoginSupport.Middleware
import Web.Controller.Sessions

-- Controller Imports
import Web.Controller.Users
import Web.Controller.Comments
import Web.Controller.Posts
import IHP.Welcome.Controller

instance FrontController WebApplication where
controllers =
[ startPage WelcomeAction
, parseRoute @SessionsController
-- Generator Marker
, parseRoute @UsersController
, parseRoute @CommentsController
, parseRoute @PostsController
]

instance InitControllerContext WebApplication
instance InitControllerContext WebApplication where
initContext =
initAuthentication @User
5 changes: 5 additions & 0 deletions Web/Routes.hs
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,8 @@ type instance ModelControllerMap WebApplication Post = PostsController
instance AutoRoute CommentsController
type instance ModelControllerMap WebApplication Comment = CommentsController

instance AutoRoute SessionsController

instance AutoRoute UsersController
type instance ModelControllerMap WebApplication User = UsersController

21 changes: 21 additions & 0 deletions Web/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ import IHP.ModelSupport
import Application.Helper.Controller
import IHP.ViewSupport
import Generated.Types
import IHP.LoginSupport.Types

instance HasNewSessionUrl User where
newSessionUrl _ = "/NewSession"

data WebApplication = WebApplication deriving (Eq, Show)

Expand All @@ -14,6 +18,7 @@ data ViewContext = ViewContext
, flashMessages :: [IHP.Controller.Session.FlashMessage]
, controllerContext :: ControllerSupport.ControllerContext
, layout :: Layout
, user :: Maybe User
}

data PostsController
Expand All @@ -35,3 +40,19 @@ data CommentsController
| UpdateCommentAction { commentId :: !(Id Comment) }
| DeleteCommentAction { commentId :: !(Id Comment) }
deriving (Eq, Show, Data)

data SessionsController
= NewSessionAction
| CreateSessionAction
| DeleteSessionAction
deriving (Eq, Show, Data)

data UsersController
= UsersAction
| NewUserAction
| ShowUserAction { userId :: !(Id User) }
| CreateUserAction
| EditUserAction { userId :: !(Id User) }
| UpdateUserAction { userId :: !(Id User) }
| DeleteUserAction { userId :: !(Id User) }
deriving (Eq, Show, Data)
3 changes: 2 additions & 1 deletion Web/View/Context.hs
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@ import Generated.Types
import qualified IHP.ViewSupport as ViewSupport
import Web.View.Layout
import Web.Types
import IHP.LoginSupport.Helper.Controller

instance ViewSupport.CreateViewContext ViewContext where
type ViewApp ViewContext = WebApplication
createViewContext = do
flashMessages <- IHP.Controller.Session.getAndClearFlashMessages
let viewContext = ViewContext {
requestContext = ?requestContext,
-- user = currentUserOrNothing,
user = currentUserOrNothing,
flashMessages,
controllerContext = ?controllerContext,
layout = let ?viewContext = viewContext in defaultLayout
Expand Down
25 changes: 25 additions & 0 deletions Web/View/Layout.hs
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,38 @@ defaultLayout inner = H.docTypeHtml ! A.lang "en" $ [hsx|
<title>App</title>
</head>
<body>
{navbar}
<div class="container mt-4">
{renderFlashMessages}
{inner}
</div>
</body>
|]

navbar :: Html
navbar = [hsx|
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<a class="navbar-brand" href="#">IHP Blog</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>

<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav mr-auto">
<li class="nav-item">
<a class="nav-link" href={PostsAction}>Posts</a>
</li>
</ul>
{loginLogoutButton}
</div>
</nav>
|]
where
loginLogoutButton :: Html
loginLogoutButton = case (get #user viewContext) of
Just user -> [hsx|<a class="js-delete js-delete-no-confirm text-secondary" href={DeleteSessionAction}>Logout</a>|]
Nothing -> [hsx|<a class="text-secondary" href={NewSessionAction}>Login</a>|]

scripts = do
when (isDevelopment FrameworkConfig.environment) [hsx|<script id="livereload-script" src="/livereload.js"></script>|]
[hsx|
Expand Down