Skip to content

Commit 8692adc

Browse files
committed
authentication
1 parent a2a47e3 commit 8692adc

File tree

14 files changed

+241
-7
lines changed

14 files changed

+241
-7
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
CREATE TABLE users (
2+
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
3+
email TEXT NOT NULL,
4+
password_hash TEXT NOT NULL,
5+
locked_at TIMESTAMP WITH TIME ZONE DEFAULT null,
6+
failed_login_attempts INT DEFAULT 0 NOT NULL,
7+
logins INT NOT NULL,
8+
isconfirmed BOOLEAN DEFAULT false NOT NULL
9+
);

Application/Schema.sql

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,13 @@ CREATE TABLE comments (
1515
);
1616
CREATE INDEX comments_post_id_index ON comments (post_id);
1717
CREATE INDEX comments_created_at_index ON comments (created_at);
18-
ALTER TABLE comments ADD CONSTRAINT comments_ref_post_id FOREIGN KEY (post_id) REFERENCES posts (id) ON DELETE NO ACTION;
19-
2018
CREATE TABLE users (
2119
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
2220
email TEXT NOT NULL,
2321
password_hash TEXT NOT NULL,
2422
locked_at TIMESTAMP WITH TIME ZONE DEFAULT NULL,
25-
failed_login_attempts INT DEFAULT 0 NOT NULL
23+
failed_login_attempts INT DEFAULT 0 NOT NULL,
24+
logins INT NOT NULL,
25+
"isConfirmed" BOOLEAN DEFAULT false NOT NULL
2626
);
27+
ALTER TABLE comments ADD CONSTRAINT comments_ref_post_id FOREIGN KEY (post_id) REFERENCES posts (id) ON DELETE NO ACTION;

Web/Controller/Posts.hs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import qualified Text.MMark as MMark
99

1010

1111
instance Controller PostsController where
12+
beforeAction = ensureIsUser
13+
1214
action PostsAction = do
1315
posts <- query @Post
1416
|> orderByDesc #createdAt
@@ -27,6 +29,7 @@ instance Controller PostsController where
2729

2830
action EditPostAction { postId } = do
2931
post <- fetch postId
32+
-- accessDeniedUnless (post.userId == currentUserId)
3033
render EditView { .. }
3134

3235
action UpdatePostAction { postId } = do

Web/Controller/Sessions.hs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,22 @@ instance Controller SessionsController where
99
action CreateSessionAction = Sessions.createSessionAction @User
1010
action DeleteSessionAction = Sessions.deleteSessionAction @User
1111

12-
instance Sessions.SessionsControllerConfig User
12+
13+
instance Sessions.SessionsControllerConfig User
14+
15+
-- action MyAction = do
16+
-- case currentUserOrNothing of
17+
-- Just currentUser -> do
18+
-- let text = "Hello " <> currentUser.email
19+
-- renderPlain text
20+
-- Nothing -> renderPlain "Please login first"
21+
22+
23+
-- instance Sessions.SessionsControllerConfig User where
24+
-- beforeLogin = updateLoginHistory
25+
26+
-- updateLoginHistory user = do
27+
-- user
28+
-- |> modify #logins (\count -> count + 1)
29+
-- |> updateRecord
30+
-- pure ()

Web/Controller/Users.hs

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
module Web.Controller.Users where
2+
3+
import Web.Controller.Prelude
4+
import Web.View.Users.Index
5+
import Web.View.Users.New
6+
import Web.View.Users.Edit
7+
import Web.View.Users.Show
8+
9+
import qualified Web.Controller.Sessions ()
10+
11+
-- instance Confirmations.ConfirmationsControllerConfig User where
12+
13+
instance Controller UsersController where
14+
action UsersAction = do
15+
users <- query @User |> fetch
16+
render IndexView { .. }
17+
18+
action NewUserAction = do
19+
let user = newRecord
20+
render NewView { .. }
21+
22+
action ShowUserAction { userId } = do
23+
user <- fetch userId
24+
render ShowView { .. }
25+
26+
action EditUserAction { userId } = do
27+
user <- fetch userId
28+
render EditView { .. }
29+
30+
action UpdateUserAction { userId } = do
31+
user <- fetch userId
32+
user
33+
|> buildUser
34+
|> ifValid \case
35+
Left user -> render EditView { .. }
36+
Right user -> do
37+
user <- user |> updateRecord
38+
setSuccessMessage "User updated"
39+
redirectTo EditUserAction { .. }
40+
41+
action CreateUserAction = do
42+
let user = newRecord @User
43+
-- The value from the password confirmation input field.
44+
let passwordConfirmation = param @Text "passwordConfirmation"
45+
user
46+
|> fill @["email", "passwordHash"]
47+
-- We ensure that the error message doesn't include
48+
-- the entered password.
49+
|> validateField #passwordHash (isEqual passwordConfirmation |> withCustomErrorMessage "Passwords don't match")
50+
|> validateField #passwordHash nonEmpty
51+
|> validateField #email isEmail
52+
-- After this validation, since it's operation on the IO, we'll need to use >>=.
53+
|> validateIsUnique #email
54+
>>= ifValid \case
55+
Left user -> render NewView { .. }
56+
Right user -> do
57+
hashed <- hashPassword user.passwordHash
58+
user <- user
59+
|> set #passwordHash hashed
60+
|> createRecord
61+
setSuccessMessage "You have registered successfully"
62+
redirectToPath "/"
63+
64+
action DeleteUserAction { userId } = do
65+
user <- fetch userId
66+
deleteRecord user
67+
setSuccessMessage "User deleted"
68+
redirectTo UsersAction
69+
70+
-- action ConfirmUserAction { userId, confirmationToken } = Confirmations.confirmAction userId confirmationToken
71+
72+
73+
buildUser user = user
74+
|> fill @'["email", "passwordHash", "failedLoginAttempts", "logins", "isconfirmed"]
75+

Web/FrontController.hs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import IHP.LoginSupport.Middleware
88
import Web.Controller.Sessions
99

1010
-- Controller Imports
11+
import Web.Controller.Users
1112
import Web.Controller.Comments
1213
import Web.Controller.Posts
1314
import Web.Controller.Static
@@ -16,6 +17,7 @@ instance FrontController WebApplication where
1617
controllers =
1718
[ startPage WelcomeAction
1819
-- Generator Marker
20+
, parseRoute @UsersController
1921
, parseRoute @CommentsController
2022
, parseRoute @PostsController
2123
, parseRoute @SessionsController

Web/Routes.hs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,6 @@ instance AutoRoute CommentsController
1212
instance AutoRoute SessionsController
1313

1414

15+
16+
instance AutoRoute UsersController
17+

Web/Types.hs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,14 @@ data SessionsController
4141
= NewSessionAction
4242
| CreateSessionAction
4343
| DeleteSessionAction
44-
deriving (Eq, Show, Data)
44+
deriving (Eq, Show, Data)
45+
data UsersController
46+
= UsersAction
47+
| NewUserAction
48+
| ShowUserAction { userId :: !(Id User) }
49+
| CreateUserAction
50+
| EditUserAction { userId :: !(Id User) }
51+
| UpdateUserAction { userId :: !(Id User) }
52+
| DeleteUserAction { userId :: !(Id User) }
53+
-- | ConfirmUserAction { userId :: !(Id User), confirmationToken :: !Text } -- <--- ADD THIS ACTION
54+
deriving (Eq, Show, Data)

Web/View/Posts/Show.hs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ renderMarkdown text =
1212
instance View ShowView where
1313
html ShowView { .. } = [hsx|
1414
{breadcrumb}
15+
<h1>Hello {currentUser.email}</h1>
16+
1517
<h1>{post.title}</h1>
1618
<p>{post.createdAt |> timeAgo}</p>
1719
<div>{post.body |> renderMarkdown}</div>
@@ -22,6 +24,8 @@ instance View ShowView where
2224

2325

2426

27+
28+
2529

2630
|]
2731
where

Web/View/Static/Welcome.hs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ data WelcomeView = WelcomeView
55

66
instance View WelcomeView where
77
html WelcomeView = [hsx|
8-
<div style="background-color: #657b83; padding: 2rem; color:hsla(196, 13%, 96%, 1); border-radius: 4px">
8+
<div style="background-color: #ea8658ff; padding: 2rem; color:hsla(196, 13%, 96%, 1); border-radius: 4px">
99
<div style="max-width: 800px; margin-left: auto; margin-right: auto">
1010
<h1 style="margin-bottom: 2rem; font-size: 2rem; font-weight: 300; border-bottom: 1px solid white; padding-bottom: 0.25rem; border-color: hsla(196, 13%, 60%, 1)">
11-
IHP
11+
1212
</h1>
1313

1414
<h2 style="margin-top: 0; margin-bottom: 0rem; font-weight: 900; font-size: 3rem">

0 commit comments

Comments
 (0)