Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
haskell-gargantext
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
150
Issues
150
List
Board
Labels
Milestones
Merge Requests
4
Merge Requests
4
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
gargantext
haskell-gargantext
Commits
e7ad54d4
Verified
Commit
e7ad54d4
authored
May 25, 2024
by
Przemyslaw Kaminski
1
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[ws] implement jwt token authorization
parent
200f3b52
Pipeline
#6140
failed with stages
in 82 minutes and 23 seconds
Changes
3
Pipelines
1
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
57 additions
and
14 deletions
+57
-14
Types.hs
src/Gargantext/API/Admin/Auth/Types.hs
+1
-1
Settings.hs
src/Gargantext/API/Admin/Settings.hs
+1
-1
Dispatcher.hs
src/Gargantext/Core/AsyncUpdates/Dispatcher.hs
+55
-12
No files found.
src/Gargantext/API/Admin/Auth/Types.hs
View file @
e7ad54d4
...
...
@@ -48,7 +48,7 @@ data CheckAuth = InvalidUser | InvalidPassword | Valid Token TreeId UserId
data
AuthenticatedUser
=
AuthenticatedUser
{
_auth_node_id
::
NodeId
,
_auth_user_id
::
UserId
}
deriving
(
Generic
)
}
deriving
(
Generic
,
Show
,
Eq
)
makeLenses
''
A
uthenticatedUser
...
...
src/Gargantext/API/Admin/Settings.hs
View file @
e7ad54d4
...
...
@@ -205,7 +205,7 @@ newEnv logger port file = do
!
central_exchange
<-
forkIO
CE
.
gServer
!
dispatcher
<-
D
.
dispatcher
!
dispatcher
<-
D
.
dispatcher
settings'
{- An 'Env' by default doesn't have strict fields, but when constructing one in production
we want to force them to WHNF to avoid accumulating unnecessary thunks.
...
...
src/Gargantext/Core/AsyncUpdates/Dispatcher.hs
View file @
e7ad54d4
...
...
@@ -26,6 +26,9 @@ import Data.Aeson.Types (prependFailure, typeMismatch)
import
Data.ByteString.Char8
qualified
as
C
import
Data.ByteString.Lazy
qualified
as
BSL
import
Data.List
(
nubBy
)
import
Data.UUID.V4
as
UUID
import
Gargantext.API.Admin.Auth.Types
(
AuthenticatedUser
(
_auth_user_id
))
import
Gargantext.API.Admin.Types
(
jwtSettings
,
Settings
,
jwtSettings
)
import
Gargantext.Core.AsyncUpdates.CentralExchange.Types
qualified
as
CETypes
import
Gargantext.Core.Types
(
NodeId
,
UserId
)
import
Gargantext.Prelude
...
...
@@ -34,6 +37,7 @@ import Nanomsg
import
Network.WebSockets
qualified
as
WS
import
Servant
import
Servant.API.WebSocket
qualified
as
WS
import
Servant.Auth.Server
(
verifyJWT
)
{-
...
...
@@ -101,6 +105,9 @@ showSub sub =
subKey
::
Subscription
->
ByteString
subKey
sub
=
wsKey
$
s_ws_key_connection
sub
type
Token
=
Text
{-
We accept requests for subscription/unsubscription via websocket.
...
...
@@ -115,6 +122,8 @@ browser.
data
WSRequest
=
WSSubscribe
Topic
|
WSUnsubscribe
Topic
|
WSAuthorize
Token
|
WSDeauthorize
|
WSPing
|
WSPong
deriving
(
Eq
,
Show
)
...
...
@@ -128,6 +137,10 @@ instance FromJSON WSRequest where
"unsubscribe"
->
do
topic
<-
o
.:
"topic"
pure
$
WSUnsubscribe
topic
"authorize"
->
do
token
<-
o
.:
"token"
pure
$
WSAuthorize
token
"deauthorize"
->
pure
$
WSDeauthorize
"ping"
->
pure
WSPing
"pong"
->
pure
WSPong
s
->
prependFailure
"parsing request type failed, "
(
typeMismatch
"request"
s
)
...
...
@@ -139,11 +152,11 @@ data Dispatcher =
}
dispatcher
::
IO
Dispatcher
dispatcher
=
do
dispatcher
::
Settings
->
IO
Dispatcher
dispatcher
authSettings
=
do
subscriptions
<-
newTVarIO
(
[]
::
[
Subscription
])
let
server
=
wsServer
subscriptions
let
server
=
wsServer
authSettings
subscriptions
d_ce_listener
<-
forkIO
(
ce_listener
subscriptions
)
...
...
@@ -181,8 +194,8 @@ removeSubscriptionsForWSKey subscriptions ws =
type
WSAPI
=
"ws"
:>
WS
.
WebSocketPending
wsServer
::
TVar
[
Subscription
]
->
Server
WSAPI
wsServer
subscriptions
=
streamData
wsServer
::
Settings
->
TVar
[
Subscription
]
->
Server
WSAPI
wsServer
authSettings
subscriptions
=
streamData
where
streamData
::
MonadIO
m
=>
WS
.
PendingConnection
->
m
()
streamData
pc
=
do
...
...
@@ -191,7 +204,11 @@ wsServer subscriptions = streamData
-- some unique, Sec-WebSocket-Key string. We use this to compare
-- connections (WS.Connection doesn't implement an Eq instance).
let
mKey
=
head
$
filter
(
\
(
k
,
_
)
->
k
==
"Sec-WebSocket-Key"
)
$
WS
.
requestHeaders
reqHead
let
key
=
snd
$
fromMaybe
(
panicTrace
"Sec-WebSocket-Key not found!"
)
mKey
let
key'
=
snd
$
fromMaybe
(
panicTrace
"Sec-WebSocket-Key not found!"
)
mKey
-- Unfortunately, a single browsers sends the same
-- Sec-WebSocket-Key so we want to make that even more unique.
uuid
<-
liftIO
$
UUID
.
nextRandom
let
key
=
key'
<>
"-"
<>
show
uuid
putText
$
show
$
WS
.
requestHeaders
reqHead
c
<-
liftIO
$
WS
.
acceptRequest
pc
let
ws
=
WSKeyConnection
(
key
,
c
)
...
...
@@ -206,19 +223,26 @@ wsServer subscriptions = streamData
wsLoop
ws
=
flip
finally
disconnect
$
do
putText
"[wsLoop] connecting"
forever
$
do
wsLoop'
CUPublic
where
wsLoop'
user
=
do
dm
<-
WS
.
receiveDataMessage
(
wsConn
ws
)
case
dm
of
newUser
<-
case
dm
of
WS
.
Text
dm'
_
->
do
case
Aeson
.
decode
dm'
of
Nothing
->
putText
"[wsLoop] unknown message"
Nothing
->
do
putText
"[wsLoop] unknown message"
return
user
Just
(
WSSubscribe
topic
)
->
do
-- TODO Fix s_connected_user based on header
let
sub
=
Subscription
{
s_connected_user
=
CUPublic
let
sub
=
Subscription
{
s_connected_user
=
user
,
s_ws_key_connection
=
ws
,
s_topic
=
topic
}
ss
<-
insertSubscription
subscriptions
sub
putText
$
"[wsLoop] subscriptions: "
<>
show
(
showSub
<$>
ss
)
return
user
Just
(
WSUnsubscribe
topic
)
->
do
-- TODO Fix s_connected_user based on header
let
sub
=
Subscription
{
s_connected_user
=
CUPublic
...
...
@@ -226,12 +250,31 @@ wsServer subscriptions = streamData
,
s_topic
=
topic
}
ss
<-
removeSubscription
subscriptions
sub
putText
$
"[wsLoop] subscriptions: "
<>
show
(
showSub
<$>
ss
)
return
user
Just
(
WSAuthorize
token
)
->
do
let
jwtS
=
authSettings
^.
jwtSettings
mUser
<-
liftBase
$
verifyJWT
jwtS
(
encodeUtf8
token
)
putText
$
"[wsLoop] authorized user: "
<>
show
mUser
-- TODO Update my subscriptions!
return
$
fromMaybe
user
(
CUUser
.
_auth_user_id
<$>
mUser
)
Just
WSDeauthorize
->
do
-- TODO Update my subscriptions!
pure
CUPublic
Just
WSPing
->
do
WS
.
sendDataMessage
(
wsConn
ws
)
(
WS
.
Text
(
Aeson
.
encode
Pong
)
Nothing
)
return
user
Just
WSPong
->
do
putText
$
"[wsLoop] pong received"
_
->
putText
"[wsLoop] binary ws messages not supported"
where
return
user
_
->
do
putText
"[wsLoop] binary ws messages not supported"
return
user
wsLoop'
newUser
disconnect
=
do
putText
"[wsLoop] disconnecting..."
ss
<-
removeSubscriptionsForWSKey
subscriptions
ws
...
...
Przemyslaw Kaminski
@cgenie
mentioned in commit
5660aec0
·
Oct 08, 2024
mentioned in commit
5660aec0
mentioned in commit 5660aec07ec5a0a0a5468f440092c1a8f57a864e
Toggle commit list
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment