shrub/tests/sys/vane/eyre.hoon
fang bf64e94ca3
tests: expand eyre eauth tests
For the server case, we add tests for http-first finalization, incorrect
eauth tokens, attempt expiration, client abort signalling, and client
deletion requests.

For the client case, we add a test for approving eauth attempts without
being locally authenticated.

To this end, we refactor the layout of the eauth tests. The +eauth core
now contains the tasks/gifts to be handled by the client/server, but no
longer does move checking. This happens within the test arms only. This
way, we may trivially re-run parts of the eauth flow under different
conditions.
2023-05-23 20:32:24 +02:00

1560 lines
45 KiB
Plaintext

/+ *test
/= eyre-raw /sys/vane/eyre
::
!:
=/ eyre-gate (eyre-raw ~nul)
=/ eyre-id '~.eyre_0v4.elsnk.20412.0h04v.50lom.5lq0o'
::
:: mare: a monad for testing eyre
::
|%
::
++ form-raw
|$ [a]
$-(state (output-raw a))
::
++ state
$: gate=_eyre-gate
now=@da
==
::
++ output-raw
|$ [a]
(each [out=a =state] tang)
::
++ mare
|* a=mold
|%
++ ouptut (output-raw a)
++ form (form-raw a)
++ pure
|= arg=a
^- form
|= =state
[%& arg state]
::
++ bind
|* b=mold
|= [m-b=(form-raw b) fun=$-(b form)]
^- form
|= =state
=/ b-res=(output-raw b) (m-b state)
?- -.b-res
%& ((fun out.p.b-res) state.p.b-res)
%| [%| p.b-res]
==
--
::
:: mario: helpers for mare
::
++ move move:eyre-gate
:: advance time
::
++ wait
|= =@dr
=/ m (mare ,~)
^- form:m
|= =state
[%& ~ state(now (add now.state dr))]
::
++ get-now
=/ m (mare ,@da)
^- form:m
|= =state
[%& now.state state]
:: raise failure
::
++ fail
|= =tang
|= =state
[%| tang]
:: fail if tang is non-null
::
++ try
|= =tang
=/ m (mare ,~)
^- form:m
?~ tang
(pure:m ~)
(fail tang)
::
++ call
|= [=duct wrapped-task=(hobo task:eyre-gate)]
=/ m (mare ,(list move))
^- form:m
|= =state
=/ eyre-core
%: gate.state
now=now.state
eny=`@uvJ`0xdead.beef
scry=scry-provides-code
==
=^ moves gate.state
(call:eyre-core duct ~ wrapped-task)
[%& moves state]
::
++ take
|= [=wire =duct =sign:eyre-gate]
=/ m (mare ,(list move))
^- form:m
|= =state
=/ eyre-core
%: gate.state
now=now.state
eny=`@uvJ`0xdead.beef
scry=scry-provides-code
==
=^ moves gate.state
(take:eyre-core wire duct ~ sign)
[%& moves state]
::
++ get
|= [url=@t =header-list:http]
=/ m (mare ,(list move))
^- form:m
%+ call ~[/http-blah]
[%request %.n [%ipv4 .192.168.1.1] [%'GET' url header-list ~]]
::
++ post
|= [url=@t =header-list:http body=@t]
=/ m (mare ,(list move))
^- form:m
=/ body (as-octs:mimes:html body)
%+ call ~[/http-blah]
[%request %.n [%ipv4 .192.168.1.1] [%'POST' url header-list `body]]
::
++ put
|= [url=@t =header-list:http body=@t]
=/ m (mare ,(list move))
^- form:m
=/ body (as-octs:mimes:html body)
%+ call ~[/http-blah]
[%request %.n [%ipv4 .192.168.1.1] [%'PUT' url header-list `body]]
:: use different wire
::
++ put-2
|= [url=@t =header-list:http body=@t]
=/ m (mare ,(list move))
^- form:m
=/ body (as-octs:mimes:html body)
%+ call ~[/http-put-request]
[%request %.n [%ipv4 .192.168.1.1] [%'PUT' url header-list `body]]
::
++ connect
|= [app=@t pax=path]
=/ m (mare ,~)
^- form:m
;< mos=(list move) bind:m (call ~[/[app]] [%connect [~ pax] app])
(expect-moves mos (ex ~[/[app]] %give %bound %.y [~ pax]) ~)
::
++ request
|= [app=@t pax=path]
=/ m (mare ,~)
=/ target (crip (spud pax))
;< mos=(list move) bind:m (get target ~)
=/ mov-1
%^ ex-gall-deal /watch-response/[eyre-id] g-name
[app %watch /http-response/[eyre-id]]
=/ mov-2
=/ response !>([eyre-id %.n %.n [%ipv4 .192.168.1.1] [%'GET' target ~ ~]])
%^ ex-gall-deal /run-app-request/[eyre-id] g-name
[app %poke %handle-http-request response]
(expect-moves mos mov-1 mov-2 ~)
::
++ expect-moves
|= [mos=(list move) exes=(list $-(move tang))]
=/ m (mare ,~)
^- form:m
|= =state
=/ =tang
|- ^- tang
?~ exes
?~ mos
~
['got more moves than expected' ~]
?~ mos
['expected more moves than got' ~]
%+ weld
(i.exes i.mos)
$(exes t.exes, mos t.mos)
?~ tang
[%& ~ state]
[%| tang]
::
++ ex-set-config
|= =http-config:eyre
|= mov=move
^- tang
(expect-eq !>([duct=~[/unix] %give %set-config http-config]) !>(mov))
::
++ ex
|= mow=move
|= mov=move
(expect-eq !>(mow) !>(mov))
::
++ ex-rest
|= [=wire =@da]
(ex ~[/http-blah] %pass wire %b %rest da)
::
++ ex-wait
|= [=wire =@da]
(ex ~[/http-blah] %pass wire %b %wait da)
::
++ ex-sessions
|= tokens=(set @t)
|= mov=move
^- tang
(expect-eq !>([duct=~[/unix] %give %sessions tokens]) !>(mov))
::
++ ex-response
|= [status=@ud headers=header-list:http body=(unit octs)]
|= mov=move
^- tang
?. ?=([[[%http-blah ~] ~] %give %response %start * * %.y] mov)
[leaf+"expected %response, got: {<mov>}" ~]
=? headers ?=(^ body)
%+ weld headers
:~ ['content-length' (crip ((d-co:co 1) p.u.body))]
['set-cookie' g-sook]
==
;: weld
(expect-eq !>(status) !>(status-code.response-header.http-event.p.card.mov))
(expect-eq !>(body) !>(data.http-event.p.card.mov))
(expect-eq !>(headers) !>(headers.response-header.http-event.p.card.mov))
==
++ ex-start-response
|= [status=@ud headers=header-list:http body=(unit octs)]
|= mov=move
^- tang
?. ?=([[[%http-blah ~] ~] %give %response %start * * %.n] mov)
[leaf+"expected start %response, got: {<mov>}" ~]
=. headers (weld headers ~[g-head])
;: weld
(expect-eq !>(status) !>(status-code.response-header.http-event.p.card.mov))
(expect-eq !>(body) !>(data.http-event.p.card.mov))
(expect-eq !>(headers) !>(headers.response-header.http-event.p.card.mov))
==
::
++ ex-continue-response
|= [body=(unit octs) complete=?]
|= mov=move
^- tang
?. ?=([[[%http-blah ~] ~] %give %response %continue * *] mov)
[leaf+"expected continue %response, got: {<mov>}" ~]
;: weld
(expect-eq !>(body) !>(data.http-event.p.card.mov))
(expect-eq !>(complete) !>(complete.http-event.p.card.mov))
==
:: produce the 204 response to a put request
::
++ ex-204
(ex-response 204 ['set-cookie' cookie-string]~ ~)
::
++ ex-204-2
|= mov=move
?. ?=([[[%http-put-request ~] ~] %give %response %start * * %.y] mov)
[leaf+"expected %response, got: {<mov>}" ~]
=/ headers ['set-cookie' cookie-string]~
;: weld
(expect-eq !>(204) !>(status-code.response-header.http-event.p.card.mov))
(expect-eq !>(~) !>(data.http-event.p.card.mov))
(expect-eq !>(headers) !>(headers.response-header.http-event.p.card.mov))
==
::
++ ex-channel-response
|= body=@t
|= mov=move
^- tang
?. ?=([[[%http-blah ~] ~] %give %response %start * * %.n] mov)
[leaf+"expected start %response, got: {<mov>}" ~]
=/ headers
:~ ['content-type' 'text/event-stream']
['cache-control' 'no-cache']
['connection' 'keep-alive']
['set-cookie' cookie-string]
==
=/ body `(as-octs:mimes:html body)
;: weld
(expect-eq !>(200) !>(status-code.response-header.http-event.p.card.mov))
(expect-eq !>(body) !>(data.http-event.p.card.mov))
(expect-eq !>(headers) !>(headers.response-header.http-event.p.card.mov))
==
::
++ ex-gall-deal
|= [=wire our=@p app=term =deal:gall]
|= mov=move
^- tang
%+ weld (expect-eq !>(~[/http-blah]) !>(duct.mov))
(expect-gall-deal [wire [our ~nul] app deal] card.mov)
::
++ expect-gall-deal
|= $: expected=[wire=path id=sock app=term =deal:gall]
actual=(wind note:eyre-gate gift:eyre-gate)
==
^- tang
::
?. ?=(%pass -.actual)
[%leaf "bad move, not a %pass: {<actual>}"]~
::
%+ weld
(expect-eq !>(wire.expected) !>(p.actual))
::
=/ note=note:eyre-gate q.actual
?. ?=([%g %deal *] note)
[%leaf "bad move, not a %deal: {<actual>}"]~
::
%+ weld
(expect-eq !>(id.expected) !>(p.note))
::
%+ weld
(expect-eq !>(app.expected) !>(q.note))
::
?: ?=([%poke *] deal.expected)
?. ?=([%poke *] r.note)
[%leaf "expected %poke, actual {<r.note>}"]~
::
%+ weld
(expect-eq !>(p.cage.deal.expected) !>(p.cage.r.note))
:: compare the payload vases
::
(expect-eq q.cage.deal.expected q.cage.r.note)
::
?: ?=([%poke-as *] deal.expected)
?. ?=([%poke-as *] r.note)
[%leaf "expected %poke-as, actual {<r.note>}"]~
:: compare the mark type
::
%+ weld
(expect-eq !>(mark.deal.expected) !>(mark.r.note))
:: compare the cage mark
::
%+ weld
(expect-eq !>(p.cage.deal.expected) !>(p.cage.r.note))
:: compare the payload vases
::
(expect-eq q.cage.deal.expected q.cage.r.note)
::
?: ?=([%watch *] deal.expected)
?. ?=([%watch *] r.note)
[%leaf "expected %watch, actual {<r.note>}"]~
:: compare the path
::
(expect-eq !>(path.deal.expected) !>(path.r.note))
::
?: ?=([%watch-as *] deal.expected)
?. ?=([%watch-as *] r.note)
[%leaf "expected %watch-as, actual {<r.note>}"]~
:: compare the result mark
::
%+ weld
(expect-eq !>(mark.deal.expected) !>(mark.r.note))
:: compare the path
::
(expect-eq !>(path.deal.expected) !>(path.r.note))
::
?: ?=([%leave *] deal.expected)
?. ?=([%leave *] r.note)
[%leaf "expected %leave, actual {<r.note>}"]~
::
~
:: todo: handle other deals
::
[%leaf "unexpected %deal type"]~
::
++ eval-mare
=/ m (mare ,~)
|= computation=form:m
^- tang
=/ res (computation eyre-gate ~1111.1.1)
?- -.res
%& ~
%| p.res
==
::
++ scry-provides-code ^- roof
|= [gang =view =beam]
^- (unit (unit cage))
?: =(%gd view) ``noun+!>(%base)
?: &(=(%ca view) =(/gen/handler/hoon s.beam))
:+ ~ ~
vase+!>(!>(|=(* |=(* [[%404 ~] ~]))))
?: &(=(%cb view) =(/json s.beam))
:^ ~ ~ %dais
!> ^- dais:clay
|_ sam=vase
++ diff !!
++ form !!
++ join !!
++ mash !!
++ pact !!
++ vale |=(=noun !>(;;(json noun)))
--
::
?> =(%j view)
?> =(~nul p.beam)
?> =(%code q.beam)
?> =(%da -.r.beam)
?> =(/~nul s.beam)
:: This is the default code for a fakeship.
::
[~ ~ %noun !>(.~lidlut-tabwed-savheb-loslux)]
::
++ cookie-value
'urbauth-~nul=0v2.v5g1m.rr6kg.bjj3k.59t1m.qp48h'
::
++ cookie-string
%^ cat 3 cookie-value
'; Path=/; Max-Age=604800'
::
++ cookie ['cookie' cookie-value]~
::
++ g-name ~fitbur-togtep-ladnup-ronbus--tanmer-sibsyn-lavryg-ramnul
++ g-auth ['cookie' g-cook]
++ g-cook 'urbauth-~nul=0v5.gbhev.sbeh0.3rov1.o6ibh.a3t9r'
++ g-sook (cat 3 g-cook '; Path=/; Max-Age=604800')
++ g-head ['set-cookie' g-sook]
--
:: Tests
::
|%
++ test-init
(eval-mare perform-init)
::
++ test-born
(eval-mare perform-born)
::
++ test-overwrite-bindings
%- eval-mare
=/ m (mare ,~)
;< ~ bind:m perform-init
;< ~ bind:m (wait ~d1)
:: app1 binds successfully
::
;< ~ bind:m (connect %app1 /)
;< ~ bind:m (wait ~d1)
:: app2 tries to bind to the same path and succeeds
::
(connect %app2 /)
::
++ test-remove-binding
%- eval-mare
=/ m (mare ,~)
;< ~ bind:m perform-init
;< ~ bind:m (wait ~d1)
:: app1 binds successfully
::
;< ~ bind:m (connect %app1 /)
;< ~ bind:m (wait ~d1)
:: app1 unbinds
::
;< mos=(list move) bind:m (call ~[/app1] [%disconnect `/])
;< ~ bind:m (expect-moves mos ~)
;< ~ bind:m (wait ~d1)
:: app2 binds successfully
::
(connect %app2 /)
::
++ test-host-matching
;: weld
(expect !>((host-matches:eyre-gate ~ `'example.com')))
(expect !>((host-matches:eyre-gate ~ ~)))
(expect !>(!(host-matches:eyre-gate `'example.com' ~)))
(expect !>((host-matches:eyre-gate `'example.com' `'example.com')))
(expect !>(!(host-matches:eyre-gate `'example.com' `'blah.com')))
==
:: tests that when we have no match, that we fall back to the built-in 404
::
++ test-builtin-four-oh-four
%- eval-mare
=/ m (mare ,~)
;< ~ bind:m perform-init-wo-timer
;< mos=(list move) bind:m (get '/' ~)
=/ headers ['content-type' 'text/html']~
=/ body `(error-page:eyre-gate 404 %.n '/' ~)
(expect-moves mos (ex-response 404 headers body) ~)
::
++ test-basic-app-request
%- eval-mare
=/ m (mare ,~)
;< ~ bind:m perform-init-wo-timer
;< ~ bind:m (wait ~d1)
:: app1 binds successfully
::
;< ~ bind:m (connect %app1 /)
;< ~ bind:m (wait ~d1)
:: outside requests a path that app1 has bound to
::
;< ~ bind:m (request %app1 /)
;< ~ bind:m (wait ~d1)
:: theoretical outside response
::
;< mos=(list move) bind:m
=/ response !>([200 ['content-type' 'text/html']~])
=/ sign=sign:eyre-gate
[%gall %unto %fact %http-response-header response]
(take /watch-response/[eyre-id] ~[/http-blah] sign)
=/ headers ['content-type' 'text/html']~
(expect-moves mos (ex-start-response 200 headers ~) ~)
::
++ test-app-error
%- eval-mare
=/ m (mare ,~)
;< ~ bind:m perform-init-wo-timer
;< ~ bind:m (wait ~d1)
:: app1 binds successfully
::
;< ~ bind:m (connect %app1 /)
;< ~ bind:m (wait ~d1)
:: outside requests a path that app1 has bound to
::
;< ~ bind:m (request %app1 /)
;< ~ bind:m (wait ~d1)
:: the poke fails. we should relay this to the client
::
;< mos=(list move) bind:m
=/ sign=sign:eyre-gate
[%gall %unto %poke-ack ~ [%leaf "/~zod/...../app1:<[1 1].[1 20]>"]~]
(take /run-app-request/[eyre-id] ~[/http-blah] sign)
=/ mov-1 (ex-gall-deal /watch-response/[eyre-id] g-name %app1 [%leave ~])
=/ response `(internal-server-error:eyre-gate %.n '/' ~)
=/ mov-2 (ex-response 500 ['content-type' 'text/html']~ response)
(expect-moves mos mov-1 mov-2 ~)
::
++ test-multipart-app-request
%- eval-mare
=/ m (mare ,~)
;< ~ bind:m perform-init-wo-timer
;< ~ bind:m (wait ~d1)
:: app1 binds successfully
::
;< ~ bind:m (connect %app1 /)
;< ~ bind:m (wait ~d1)
:: outside requests a path that app1 has bound to
::
;< ~ bind:m (request %app1 /)
;< ~ bind:m (wait ~d1)
:: theoretical outside response
::
;< mos=(list move) bind:m
=/ response !>([200 ['content-type' 'text/html']~])
=/ sign=sign:eyre-gate
[%gall %unto %fact %http-response-header response]
(take /watch-response/[eyre-id] ~[/http-blah] sign)
;< ~ bind:m
=/ headers ['content-type' 'text/html']~
(expect-moves mos (ex-start-response 200 headers ~) ~)
;< ~ bind:m (wait ~s1)
:: 2nd response
::
;< mos=(list move) bind:m
=/ response !>(`(as-octs:mimes:html 'ya!'))
=/ sign=sign:eyre-gate
[%gall %unto %fact %http-response-data response]
(take /watch-response/[eyre-id] ~[/http-blah] sign)
=/ headers ['content-type' 'text/html']~
(expect-moves mos (ex-continue-response `[3 'ya!'] %.n) ~)
:: tests an app redirecting to the login handler, which then receives a post
:: and redirects back to app
::
++ test-login-handler-full-path
%- eval-mare
=/ m (mare ,~)
;< ~ bind:m perform-init-wo-timer
;< ~ bind:m perform-born
;< ~ bind:m (wait ~d1)
:: app1 binds successfully
::
;< ~ bind:m (connect %app1 /'~landscape')
;< ~ bind:m (wait ~d1)
:: outside requests a path that app1 has bound to
::
;< ~ bind:m (request %app1 /'~landscape')
;< ~ bind:m (wait ~d1)
:: app then gives a redirect to Eyre
::
=/ headers ['location' '/~/login?redirect=/~landscape/inner-path']~
;< mos=(list move) bind:m
=/ sign=sign:eyre-gate
[%gall %unto %fact %http-response-header !>([303 headers])]
(take /watch-response/[eyre-id] ~[/http-blah] sign)
;< ~ bind:m (expect-moves mos (ex-start-response 303 headers ~) ~)
;< ~ bind:m (wait ~d1)
:: the browser then fetches the login page
::
;< ~ bind:m perform-authentication-2
;< ~ bind:m (wait ~h1)
:: going back to the original url will acknowledge the authentication cookie
::
;< mos=(list move) bind:m
(get '/~landscape/inner-path' ['cookie' cookie-value]~)
=/ mov-1
%^ ex-gall-deal /watch-response/[eyre-id] ~nul
[%app1 %watch /http-response/[eyre-id]]
=/ mov-2
=/ request [%'GET' '/~landscape/inner-path' ['cookie' cookie-value]~ ~]
=/ response !>([eyre-id %.y %.n [%ipv4 .192.168.1.1] request])
%^ ex-gall-deal /run-app-request/[eyre-id] ~nul
[%app1 %poke %handle-http-request response]
(expect-moves mos mov-1 mov-2 ~)
::
++ test-generator
%- eval-mare
=/ m (mare ,~)
;< ~ bind:m perform-init-wo-timer
;< ~ bind:m (wait ~d1)
:: gen1 binds successfully
::
;< mos=(list move) bind:m
(call ~[/gen1] [%serve [~ /] %base /gen/handler/hoon ~])
;< ~ bind:m (expect-moves mos (ex ~[/gen1] %give %bound %.y [~ /]) ~)
;< ~ bind:m (wait ~d1)
:: outside requests a path that app1 has bound to
::
;< mos=(list move) bind:m (get '/' ~)
(expect-moves mos (ex-response 404 [g-head]~ ~) ~)
::
++ test-simplified-url-parser
;: weld
%+ expect-eq
!> `[[%site 'localhost'] [~ 8.000]]
!> (rush 'localhost:8000' simplified-url-parser:eyre-gate)
::
%+ expect-eq
!> `[[%ip .192.168.1.1] ~]
!> (rush '192.168.1.1' simplified-url-parser:eyre-gate)
==
::
++ test-parse-channel-request-jam
;: weld
%+ expect-eq
!> &+[%ack 5]~
!> %+ parse-channel-request:eyre-gate %jam
(as-octs:mimes:html (scot %uw (jam [%ack 5]~)))
::
%+ expect-eq
!> |+'invalid request data'
!> %+ parse-channel-request:eyre-gate %jam
(as-octs:mimes:html (scot %uw (jam [%not %a %chanreq %list])))
==
::
++ test-parse-channel-request-json
;: weld
%+ expect-eq
!> &+[%ack 5]~
!> %+ parse-channel-request:eyre-gate %json
(as-octs:mimes:html '[{"action": "ack", "event-id": 5}]')
::
%+ expect-eq
!> &+[%poke-json 0 ~nec %app1 %app-type [%n '5']]~
!> %+ parse-channel-request:eyre-gate %json
%- as-octs:mimes:html
'''
[{"action": "poke",
"id": 0,
"ship": "nec",
"app": "app1",
"mark": "app-type",
"json": 5}]
'''
::
%+ expect-eq
!> &+[%subscribe 1 ~sampyl-sipnym %hall /this/path]~
!> %+ parse-channel-request:eyre-gate %json
%- as-octs:mimes:html
'''
[{"action": "subscribe",
"id": 1,
"ship": "sampyl-sipnym",
"app": "hall",
"path": "/this/path"}]
'''
::
%+ expect-eq
!> &+[%unsubscribe 2 1]~
!> %+ parse-channel-request:eyre-gate %json
%- as-octs:mimes:html
'''
[{"action": "unsubscribe",
"id": 2,
"subscription": 1}]
'''
::
%+ expect-eq
!> |+'invalid channel json'
!> %+ parse-channel-request:eyre-gate %json
%- as-octs:mimes:html
'[{"noaction": "noaction"}]'
::
%+ expect-eq
!> |+'invalid channel json'
!> %+ parse-channel-request:eyre-gate %json
%- as-octs:mimes:html
'[{"action": "bad-action"}]'
::
%+ expect-eq
!> |+'invalid channel json'
!> %+ parse-channel-request:eyre-gate %json
%- as-octs:mimes:html
'[{"action": "ack", "event-id": 5}, {"action": "bad-action"}]'
::
%+ expect-eq
!> :- %&
:~ [%ack 9]
[%poke-json 3 ~bud %wut %wut-type [%a [%n '2'] [%n '1'] ~]]
==
!> %+ parse-channel-request:eyre-gate %json
%- as-octs:mimes:html
'''
[{"action": "ack", "event-id": 9},
{"action": "poke",
"id": 3,
"ship": "bud",
"app": "wut",
"mark": "wut-type",
"json": [2, 1]}]
'''
==
::
++ test-channel-open-never-used-expire
%- eval-mare
=/ m (mare ,~)
;< ~ bind:m perform-init-start-channel-2
:: the behn timer wakes us up; we cancel our subscription
::
;< ~ bind:m (wait ~h12)
=/ wire /channel/timeout/'0123456789abcdef'
;< mos=(list move) bind:m (take wire ~[/http-blah] %behn %wake ~)
=/ wire /channel/subscription/'0123456789abcdef'/1/~nul/two
(expect-moves mos (ex-gall-deal wire ~nul %two %leave ~) ~)
::
++ test-channel-results-before-open
%- eval-mare
=/ m (mare ,~)
;< ~ bind:m perform-init-start-channel-2
;< ~ bind:m (wait ~m1)
:: poke gets a success message
::
=/ wire /channel/poke/'0123456789abcdef'/'0'
;< mos=(list move) bind:m (take wire ~[/http-blah] %gall %unto %poke-ack ~)
;< ~ bind:m (expect-moves mos ~)
:: subscription gets a success message
::
=/ wire /channel/subscription/'0123456789abcdef'/'1'/~nul/two
;< mos=(list move) bind:m
(take wire ~[/http-blah] %gall %unto %watch-ack ~)
;< ~ bind:m (expect-moves mos ~)
:: subscription gets a result
::
;< ~ bind:m (wait ~m1)
;< mos=(list move) bind:m
=/ =cage [%json !>(`json`[%a [%n '1'] [%n '2'] ~])]
(take wire ~[/http-blah] %gall %unto %fact cage)
;< ~ bind:m (expect-moves mos ~)
:: open up the channel
::
:: send the channel a poke and a subscription request
::
;< ~ bind:m (wait ~m1)
;< mos=(list move) bind:m
(get '/~/channel/0123456789abcdef' cookie)
;< now=@da bind:m get-now
=/ mov-1 (ex-wait /channel/heartbeat/'0123456789abcdef' (add now ~s20))
=/ mov-2
%- ex-channel-response
'''
id: 0
data: {"ok":"ok","id":0,"response":"poke"}
id: 1
data: {"ok":"ok","id":1,"response":"subscribe"}
id: 2
data: {"json":[1,2],"id":1,"response":"diff"}
'''
:: opening the channel cancels the timeout timer
::
=/ mov-3 (ex-rest /channel/timeout/'0123456789abcdef' ~1111.1.2..12.00.00)
;< ~ bind:m (expect-moves mos mov-1 mov-2 mov-3 ~)
:: we get a cancel when we notice the client has disconnected
::
;< ~ bind:m (wait ~m1)
;< mos=(list move) bind:m (call ~[/http-blah] %cancel-request ~)
=/ mov-1
(ex-rest /channel/heartbeat/'0123456789abcdef' :(add ~1111.1.2 ~m3 ~s20))
=/ mov-2
(ex-wait /channel/timeout/'0123456789abcdef' :(add ~1111.1.2 ~m4 ~h12))
(expect-moves mos mov-1 mov-2 ~)
::
++ test-channel-second-get-updates-timer
%- eval-mare
=/ m (mare ,~)
;< ~ bind:m perform-init-start-channel-2
;< ~ bind:m (wait ~m1)
:: perform another poke to a different app
::
:: Since we haven't connected with a GET, the old timer should be canceled
:: and a new one should be set.
::
;< mos=(list move) bind:m
%^ put '/~/channel/0123456789abcdef' cookie
'''
[{"action": "poke",
"id": 2,
"ship": "nul",
"app": "eight",
"mark": "a",
"json": 9}]
'''
=/ wire /channel/poke/'0123456789abcdef'/'2'
=/ mov-1 (ex-gall-deal wire ~nul %eight %poke-as %a %json !>([%n '9']))
=/ mov-2 ex-204
=/ mov-3 (ex-rest /channel/timeout/'0123456789abcdef' ~1111.1.2..12.00.00)
=/ mov-4 (ex-wait /channel/timeout/'0123456789abcdef' ~1111.1.2..12.01.00)
(expect-moves mos mov-1 mov-2 mov-3 mov-4 ~)
::
++ test-channel-unsubscribe-stops-events
%- eval-mare
=/ m (mare ,~)
;< ~ bind:m perform-init-start-channel-2
;< ~ bind:m (wait ~m1)
:: poke gets a success message
::
=/ wire /channel/poke/'0123456789abcdef'/'0'
;< mos=(list move) bind:m (take wire ~[/http-blah] %gall %unto %poke-ack ~)
;< ~ bind:m (expect-moves mos ~)
;< ~ bind:m (wait ~m1)
:: subscription gets a success message
::
=/ wire /channel/subscription/'0123456789abcdef'/'1'/~nul/two
;< mos=(list move) bind:m
(take wire ~[/http-blah] %gall %unto %watch-ack ~)
;< ~ bind:m (expect-moves mos ~)
;< ~ bind:m (wait ~m1)
:: sending an unsubscribe sends an unsubscribe to gall
::
;< mos=(list move) bind:m
%^ put '/~/channel/0123456789abcdef' cookie
'''
[{"action": "unsubscribe",
"id": 2,
"subscription": 1}
]
'''
=/ wire /channel/subscription/'0123456789abcdef'/'1'/~nul/two
=/ mov-1 (ex-gall-deal wire ~nul %two %leave ~)
=/ mov-2 ex-204
=/ mov-3 (ex-rest /channel/timeout/'0123456789abcdef' ~1111.1.2..12.00.00)
=/ mov-4 (ex-wait /channel/timeout/'0123456789abcdef' ~1111.1.2..12.03.00)
(expect-moves mos mov-1 mov-2 mov-3 mov-4 ~)
::
++ test-channel-double-subscription-works
%- eval-mare
=/ m (mare ,~)
;< ~ bind:m perform-init-start-channel-2
;< ~ bind:m (wait ~m1)
:: poke gets a success message
::
=/ wire /channel/poke/'0123456789abcdef'/'0'
;< mos=(list move) bind:m (take wire ~[/http-blah] %gall %unto %poke-ack ~)
;< ~ bind:m (expect-moves mos ~)
;< ~ bind:m (wait ~m1)
:: subscription gets a success message
::
=/ wire /channel/subscription/'0123456789abcdef'/'1'/~nul/two
;< mos=(list move) bind:m
(take wire ~[/http-blah] %gall %unto %watch-ack ~)
;< ~ bind:m (expect-moves mos ~)
;< ~ bind:m (wait ~m1)
:: now make a second subscription from the client on the same path
::
;< mos=(list move) bind:m
%^ put '/~/channel/0123456789abcdef' cookie
'''
[{"action": "subscribe",
"id": 2,
"ship": "nul",
"app": "two",
"path": "/one/two/three"}
]
'''
=/ wire /channel/subscription/'0123456789abcdef'/'2'/~nul/two
=/ mov-1 (ex-gall-deal wire ~nul %two %watch /one/two/three)
=/ mov-2 ex-204
=/ mov-3 (ex-rest /channel/timeout/'0123456789abcdef' ~1111.1.2..12.00.00)
=/ mov-4 (ex-wait /channel/timeout/'0123456789abcdef' ~1111.1.2..12.03.00)
:: subscription gets 2 results
::
;< mos=(list move) bind:m
=/ wire /channel/subscription/'0123456789abcdef'/'1'/~nul/two
=/ =cage [%json !>(`json`[%a [%n '1'] [%n '2'] ~])]
(take wire ~[/http-blah] %gall %unto %fact cage)
;< ~ bind:m (expect-moves mos ~)
::
;< mos=(list move) bind:m
=/ wire /channel/subscription/'0123456789abcdef'/'2'/~nul/two
=/ =cage [%json !>(`json`[%a [%n '1'] [%n '2'] ~])]
(take wire ~[/http-blah] %gall %unto %fact cage)
;< ~ bind:m (expect-moves mos ~)
:: open up the channel
::
;< mos=(list move) bind:m
(get '/~/channel/0123456789abcdef' cookie)
;< now=@da bind:m get-now
=/ mov-1 (ex-wait /channel/heartbeat/'0123456789abcdef' (add now ~s20))
=/ mov-2
%- ex-channel-response
'''
id: 0
data: {"ok":"ok","id":0,"response":"poke"}
id: 1
data: {"ok":"ok","id":1,"response":"subscribe"}
id: 2
data: {"json":[1,2],"id":1,"response":"diff"}
id: 3
data: {"json":[1,2],"id":2,"response":"diff"}
'''
:: opening the channel cancels the timeout timer
::
=/ mov-3 (ex-rest /channel/timeout/'0123456789abcdef' ~1111.1.2..12.03.00)
;< ~ bind:m (expect-moves mos mov-1 mov-2 mov-3 ~)
:: we can close the first channel without closing the second
::
;< mos=(list move) bind:m
%^ put '/~/channel/0123456789abcdef' cookie
'''
[{"action": "unsubscribe",
"id": 3,
"subscription": 1}
]
'''
=/ wire /channel/subscription/'0123456789abcdef'/'1'/~nul/two
=/ mov-1 (ex-gall-deal wire ~nul %two %leave ~)
=/ mov-2 ex-204
;< ~ bind:m (expect-moves mos mov-1 mov-2 ~)
:: gall responds on the second subscription.
::
:: This just tests that closing one of the two subscriptions doesn't
:: unsubscribe to the other.
::
;< mos=(list move) bind:m
=/ wire /channel/subscription/'0123456789abcdef'/'2'/~nul/two
=/ =cage [%json !>(`json`[%a [%n '1'] [%n '2'] ~])]
(take wire ~[/http-blah] %gall %unto %fact cage)
=/ mov-1
%- ex-continue-response :_ %.n :- ~
%- as-octs:mimes:html
'''
id: 4
data: {"json":[1,2],"id":2,"response":"diff"}
'''
(expect-moves mos mov-1 ~)
::
++ test-prune-events
=/ q=(qeu [id=@ud @ud channel-event:eyre]) ~
=. q (~(put to q) [0 0 *channel-event:eyre])
=. q (~(put to q) [1 0 *channel-event:eyre])
=. q (~(put to q) [2 0 *channel-event:eyre])
=. q (~(put to q) [3 1 *channel-event:eyre])
=. q (~(put to q) [4 1 *channel-event:eyre])
::
=^ a q (prune-events:eyre-gate q 3)
::
%+ expect-eq
!>
:- (~(gas by *(map @ud @ud)) ~[0^3 1^1])
[~ [4 1 *channel-event:eyre]]
!>([a ~(top to q)])
::
++ test-subtract-acked-events
=/ a (~(gas by *(map @ud @ud)) ~[0^3 1^1])
=/ u (~(gas by *(map @ud @ud)) ~[0^4 2^1])
=/ e (~(gas by *(map @ud @ud)) ~[0^1 2^1])
=/ r (subtract-acked-events:eyre-gate a u)
(expect-eq !>(e) !>(r))
::
++ test-channel-sends-unacknowledged-events-on-reconnection
%- eval-mare
=/ m (mare ,~)
;< ~ bind:m perform-init-start-channel-2
;< ~ bind:m (wait ~m1)
:: poke gets a success message
::
=/ wire /channel/poke/'0123456789abcdef'/'0'
;< mos=(list move) bind:m (take wire ~[/http-blah] %gall %unto %poke-ack ~)
;< ~ bind:m (expect-moves mos ~)
:: subscription gets a success message
::
=/ wire /channel/subscription/'0123456789abcdef'/'1'/~nul/two
;< mos=(list move) bind:m
(take wire ~[/http-blah] %gall %unto %watch-ack ~)
;< ~ bind:m (expect-moves mos ~)
:: open the http channel
::
;< ~ bind:m (wait ~m2)
;< mos=(list move) bind:m
(get '/~/channel/0123456789abcdef' cookie)
;< now=@da bind:m get-now
=/ heartbeat (add now ~s20)
=/ mov-1 (ex-wait /channel/heartbeat/'0123456789abcdef' heartbeat)
=/ mov-2
%- ex-channel-response
'''
id: 0
data: {"ok":"ok","id":0,"response":"poke"}
id: 1
data: {"ok":"ok","id":1,"response":"subscribe"}
'''
:: opening the channel cancels the timeout timer
::
=/ mov-3 (ex-rest /channel/timeout/'0123456789abcdef' ~1111.1.2..12.00.00)
;< ~ bind:m (expect-moves mos mov-1 mov-2 mov-3 ~)
;< ~ bind:m (wait ~m1)
:: first subscription result gets sent to the user
::
;< mos=(list move) bind:m
=/ wire /channel/subscription/'0123456789abcdef'/'1'/~nul/two
=/ =cage [%json !>(`json`[%a [%n '1'] ~])]
(take wire ~[/http-blah] %gall %unto %fact cage)
=/ mov-1
%- ex-continue-response :_ %.n :- ~
%- as-octs:mimes:html
'''
id: 2
data: {"json":[1],"id":1,"response":"diff"}
'''
;< ~ bind:m (expect-moves mos mov-1 ~)
;< ~ bind:m (wait ~m1)
:: the client now acknowledges up to event 1
::
;< mos=(list move) bind:m
%^ put-2 '/~/channel/0123456789abcdef' cookie
'''
[{"action": "ack",
"event-id": 1}
]
'''
;< ~ bind:m (expect-moves mos ex-204-2 ~)
;< ~ bind:m (wait ~m1)
:: the client connection is detected to be broken
::
;< mos=(list move) bind:m (call ~[/http-blah] %cancel-request ~)
=/ mov-1 (ex-rest /channel/heartbeat/'0123456789abcdef' heartbeat)
=/ mov-2
(ex-wait /channel/timeout/'0123456789abcdef' :(add ~1111.1.2 ~m6 ~h12))
;< ~ bind:m (expect-moves mos mov-1 mov-2 ~)
;< ~ bind:m (wait ~m1)
:: another subscription result while the user is disconnected
::
;< mos=(list move) bind:m
=/ wire /channel/subscription/'0123456789abcdef'/'1'/~nul/two
=/ =cage [%json !>(`json`[%a [%n '2'] ~])]
(take wire ~[/http-blah] %gall %unto %fact cage)
;< ~ bind:m (expect-moves mos ~)
;< ~ bind:m (wait ~m1)
:: the client now retries to connect
::
:: Because the client has acknowledged up to event 1, we should start the connection by
:: resending events 2 and 3.
::
;< mos=(list move) bind:m
(get '/~/channel/0123456789abcdef' cookie)
;< now=@da bind:m get-now
=/ heartbeat (add now ~s20)
=/ mov-1 (ex-wait /channel/heartbeat/'0123456789abcdef' heartbeat)
=/ mov-2
%- ex-channel-response
'''
id: 2
data: {"json":[1],"id":1,"response":"diff"}
id: 3
data: {"json":[2],"id":1,"response":"diff"}
'''
=/ mov-3
(ex-rest /channel/timeout/'0123456789abcdef' :(add ~1111.1.2 ~m6 ~h12))
(expect-moves mos mov-1 mov-2 mov-3 ~)
::
++ test-channel-subscription-clogged
%- eval-mare
=/ m (mare ,~)
;< ~ bind:m perform-init-start-channel-2
;< ~ bind:m (wait (add ~s1 clog-timeout:eyre-gate))
:: subscription gets a success message
::
=/ wire /channel/subscription/'0123456789abcdef'/'1'/~nul/two
;< mos=(list move) bind:m
(take wire ~[/http-blah] %gall %unto %watch-ack ~)
;< ~ bind:m (expect-moves mos ~)
:: opens the http channel
::
;< tested-elsewhere=(list move) bind:m
(get '/~/channel/0123456789abcdef' cookie)
:: user gets sent multiple subscription results
::
=/ max=@ud clog-threshold:eyre-gate
=/ cur=@ud 0
|- ^- form:m
=* loop-fact $
?. =(cur max)
;< tested-elsewhere=(list move) bind:m
=/ wire /channel/subscription/'0123456789abcdef'/'1'/~nul/two
=/ =cage [%json !>(`json`[%a [%n '1'] ~])]
(take wire ~[/http-blah] %gall %unto %fact cage)
loop-fact(cur +(cur))
:: the next subscription result should trigger a clog
::
;< mos=(list move) bind:m
=/ wire /channel/subscription/'0123456789abcdef'/'1'/~nul/two
=/ =cage [%json !>(`json`[%a [%n '1'] ~])]
(take wire ~[/http-blah] %gall %unto %fact cage)
=/ mov-1
%- ex-continue-response :_ %.n :- ~
%- as-octt:mimes:html
"""
id: {((d-co:co 1) +(clog-threshold:eyre-gate))}
data: \{"json":[1],"id":1,"response":"diff"}
"""
=/ wire /channel/subscription/'0123456789abcdef'/'1'/~nul/two
=/ mov-2 (ex-gall-deal wire ~nul %two %leave ~)
=/ mov-3
%- ex-continue-response :_ %.n :- ~
%- as-octt:mimes:html
"""
id: {((d-co:co 1) (add 2 clog-threshold:eyre-gate))}
data: \{"id":1,"response":"quit"}
"""
;< ~ bind:m (expect-moves mos mov-1 mov-2 mov-3 ~)
:: subsequent subscription updates, which might have gotten sent out during
:: the same event in which a clog triggered, should be silently ignored
::
;< mos=(list move) bind:m
=/ wire /channel/subscription/'0123456789abcdef'/'1'/~nul/two
=/ =cage [%json !>(`json`[%a [%n '1'] ~])]
(take wire ~[/http-blah] %gall %unto %fact cage)
(expect-moves mos ~)
::
++ test-born-sends-pending-cancels
%- eval-mare
=/ m (mare ,~)
;< ~ bind:m perform-init-wo-timer
;< ~ bind:m (wait ~d1)
:: app1 binds successfully
::
;< ~ bind:m (connect %app1 /)
;< ~ bind:m (wait ~d1)
:: outside requests a path that app1 has bound to
::
;< ~ bind:m (request %app1 /)
;< ~ bind:m (wait ~d1)
:: but app1 doesn't respond before our urbit gets shut down. ensure we send
:: cancels on open connections.
::
;< mos=(list move) bind:m (call ~[/born] [%born ~])
=/ mov-3 (ex-gall-deal /watch-response/[eyre-id] g-name %app1 [%leave ~])
(expect-moves mos _*tang _*tang mov-3 ~)
::
:: +perform-init: %init a new eyre-gate
::
++ perform-init
=/ m (mare ,~)
;< mos=(list move) bind:m (call ~[/init] [%init ~])
(expect-moves mos ~)
:: +perform-init-wo-timer: init, then add a guest session
::
:: so that we don't have to include the session expiry timer move
:: in every single request handling test
::
++ perform-init-wo-timer
=/ m (mare ,~)
^- form:m
;< ~ bind:m perform-init
|= =state
:+ %& ~
=- state(sessions.auth.server-state.ax.gate -)
%+ ~(put by sessions.auth.server-state.ax.gate.state)
0vguest
[fake+~sampel-sampel-sampel-sampel--sampel-sampel-sampel-sampel ~2222.2.2 ~]
::
++ setup-for-eauth
|= base=@t
=/ m (mare ,~)
^- form:m
;< ~ bind:m perform-init-wo-timer
;< ~ bind:m perform-born
;< ~ bind:m perform-authentication-2
;< ~ bind:m
:: make sure there is an eauth endpoint set
::
|= =state
&+`state(user.endpoint.auth.server-state.ax.gate `base)
(pure:m ~)
:: +perform-born: %born an eyre-gate
::
++ perform-born
=/ m (mare ,~)
^- form:m
;< mos=(list move) bind:m (call ~[/unix] [%born ~])
(expect-moves mos (ex-set-config *http-config:eyre) (ex-sessions ~) ~)
::
++ test-perform-authentication
%- eval-mare
=/ m (mare ,~)
^- form:m
;< ~ bind:m perform-init-wo-timer
;< ~ bind:m perform-born
perform-authentication-2
:: +perform-authentication: goes through the authentication flow
::
++ perform-authentication-2
=/ m (mare ,~)
^- form:m
;< mos=(list move) bind:m
(get '/~/login?redirect=/~landscape/inner-path' g-auth ~)
;< ~ bind:m
=/ headers ['content-type' 'text/html']~
=/ body `(login-page:eyre-gate `'/~landscape/inner-path' ~nul fake+g-name ~ %.n)
(expect-moves mos (ex-response 200 headers body) ~)
;< mos=(list move) bind:m
=/ body 'password=lidlut-tabwed-pillex-ridrup&redirect=/~landscape'
(post '/~/login' ~ body)
;< ~ bind:m
=/ headers ~[['location' '/~landscape'] ['set-cookie' cookie-string]]
=/ token '0v2.v5g1m.rr6kg.bjj3k.59t1m.qp48h'
(expect-moves mos (ex-sessions token ~ ~) (ex-response 303 headers ~) ~)
(pure:m ~)
::
++ eauth
|%
++ server
|%
++ nonce 0vcn5.qlgj3.hpopf
++ wire /eauth/plea/(scot %ud %0)/(scot %p ~sampel)/(scot %uv nonce)
::
++ start
=/ body 'eauth&name=~sampel&redirect=/final'
(post '/~/login' [g-auth]~ body)
::
++ acked
=/ m (mare ,[mos1=(list move) mos2=(list move)])
^- form:m
;< mos1=(list move) bind:m (take wire ~[/http-blah] %ames %done ~)
;< mos2=(list move) bind:m
%^ take wire ~[/http-blah]
[%ames %boon %0 %ack 'http://sampel.com/~/eauth']
(pure:m mos1 mos2)
::
++ grant
%^ take wire ~[/http-blah]
[%ames %boon %0 %fin 0vtoken]
::
++ final
=; url=@t (get url [g-auth]~)
(cat 3 '/~/eauth?token=0vtoken&nonce=' (scot %uv nonce))
::
::NOTE expects a version %0 plea for ~sampel with the +nonce nonce
++ ex-plea
|= [=ship plea=eauth-plea:eyre]
(ex ~[/http-blah] %pass wire %a %plea ship %e /eauth/(scot %ud %0) plea)
--
::
++ client
|%
++ start
%+ call ~[/http-blah]
[%plea ~hoster %e /eauth/0 %0 %syn 0vnonce 'http://hoster.com/~/eauth']
::
++ grant
=/ body 'server=~hoster&nonce=0vnonce&grant=grant'
(post '/~/eauth' cookie body)
::
++ ex-boon
|= boon=eauth-boon:eyre
(ex ~[/http-blah] %give %boon boon)
--
--
::
::TODO would be good to test with different ducts so we know they get
:: stored & re-used correctly
++ test-eauth-incoming
%- eval-mare
=/ m (mare ,~)
^- form:m
=, server:eauth
;< ~ bind:m (setup-for-eauth 'http://hoster.com')
:: eauth login attempt starts the flow: send a %syn, set timeout timer
::
;< mos=(list move) bind:m start
;< now=@da bind:m get-now
;< ~ bind:m
%+ expect-moves mos
:~ (ex-plea ~sampel %0 %syn nonce 'http://hoster.com/~/eauth')
(ex-wait /eauth/expire/visitors/(scot %uv nonce) (add now ~m5))
==
:: ~sampel gets back to us with an %ack boon, we redirect the requester
::
;< [mos1=(list move) mos2=(list move)] bind:m acked
;< ~ bind:m (expect-moves mos1 ~)
;< ~ bind:m
%+ expect-moves mos2
=/ loc=@t
%^ cat 3
'http://sampel.com/~/eauth?server=~nul&nonce='
(scot %uv nonce)
:~ (ex-response 303 ~['location'^loc g-head] ~)
==
:: requester approves, we get a %fin
::
;< mos=(list move) bind:m grant
;< ~ bind:m (expect-moves mos ~)
:: requester returns for the final request
::
;< mos=(list move) bind:m final
;< ~ bind:m
%+ expect-moves mos
:~ (ex-response 303 ~['location'^'/final' g-head] ~)
==
(pure:m ~)
::
++ test-eauth-incoming-slowames
%- eval-mare
=/ m (mare ,~)
^- form:m
=, server:eauth
;< ~ bind:m (setup-for-eauth 'http://hoster.com')
;< * bind:m start
;< * bind:m acked
:: requester returns for the final request before %fin comes in
::
;< mos=(list move) bind:m final
;< ~ bind:m (expect-moves mos ~)
:: requester approves, we get a %fin, we respond to the final request
::
;< mos=(list move) bind:m grant
;< ~ bind:m
%+ expect-moves mos
:~ (ex-response 303 ~['location'^'/final' g-head] ~)
==
(pure:m ~)
::
++ test-eauth-incoming-bad-token
%- eval-mare
=/ m (mare ,~)
^- form:m
=, server:eauth
;< ~ bind:m (setup-for-eauth 'http://hoster.com')
;< * bind:m start
;< * bind:m acked
;< * bind:m grant
:: requester GETs a url with a non-matching token
::
;< mos=(list move) bind:m
=; url=@t (get url [g-auth]~)
(cat 3 '/~/eauth?token=0vbad&nonce=' (scot %uv nonce))
;< ~ bind:m
%+ expect-moves mos
=/ body `(eauth-error-page:eyre-gate %server '/final')
:~ (ex-response 400 ['content-type' 'text/html']~ body)
==
(pure:m ~)
::
++ test-eauth-incoming-bad-token-slowames
%- eval-mare
=/ m (mare ,~)
^- form:m
=, server:eauth
;< ~ bind:m (setup-for-eauth 'http://hoster.com')
;< * bind:m start
;< * bind:m acked
;< * bind:m final
:: %fin comes in, but it doesn't match the token from the request
::
;< mos=(list move) bind:m
%^ take wire ~[/http-blah]
[%ames %boon %0 %fin 0vbad]
;< ~ bind:m
%+ expect-moves mos
=/ body `(eauth-error-page:eyre-gate %server '/final')
:~ (ex-response 400 ['content-type' 'text/html']~ body)
==
(pure:m ~)
::
++ test-eauth-incoming-expired
%- eval-mare
=/ m (mare ,~)
^- form:m
=, server:eauth
;< ~ bind:m (setup-for-eauth 'http://hoster.com')
;< * bind:m start
:: expiry timer fires, we serve a response and delete the attempt
::
;< ~ bind:m (wait ~m5)
;< mos=(list move) bind:m
=/ =^wire /eauth/expire/visitors/(scot %uv nonce)
(take wire ~[/http-blah] %behn %wake ~)
;< ~ bind:m
%+ expect-moves mos
=/ body `(eauth-error-page:eyre-gate %server '/final')
:~ (ex-response 503 ['content-type' 'text/html']~ body)
(ex-plea ~sampel %0 %del nonce)
(ex ~[/http-blah] %pass wire %a %cork ~sampel)
==
(pure:m ~)
::
++ test-eauth-incoming-aborted
%- eval-mare
=/ m (mare ,~)
^- form:m
=, server:eauth
;< ~ bind:m (setup-for-eauth 'http://hoster.com')
;< * bind:m start
;< * bind:m acked
:: visitor returns, saying the attempt was aborted. we delete it
::
;< mos=(list move) bind:m
=; url=@t (get url [g-auth]~)
(cat 3 '/~/eauth?abort&nonce=' (scot %uv nonce))
;< ~ bind:m
%+ expect-moves mos
=/ loc '/~/login?eauth&redirect=%2Ffinal'
:~ (ex-response 303 ~['location'^loc g-head] ~)
(ex-plea ~sampel %0 %del nonce)
(ex ~[/http-blah] %pass wire %a %cork ~sampel)
==
(pure:m ~)
::
++ test-eauth-incoming-delete
%- eval-mare
=/ m (mare ,~)
^- form:m
=, server:eauth
;< ~ bind:m (setup-for-eauth 'http://hoster.com')
;< * bind:m start
;< * bind:m acked
;< * bind:m grant
;< * bind:m final
:: visitor tells us they want the session deleted
::
;< mos=(list move) bind:m
%^ take wire ~[/http-blah]
[%ames %boon %0 %del ~]
;< ~ bind:m
%+ expect-moves mos
:~ (ex-plea ~sampel %0 %del nonce)
(ex ~[/http-blah] %pass wire %a %cork ~sampel)
==
(pure:m ~)
::
++ test-eauth-outgoing
%- eval-mare
=/ m (mare ,~)
^- form:m
=, client:eauth
;< ~ bind:m (setup-for-eauth 'http://client.com')
:: host initiates eauth flow: we get a %syn, send an %ack, set timeout
::
;< mos=(list move) bind:m start
;< now=@da bind:m get-now
;< ~ bind:m
%+ expect-moves mos
:~ (ex ~[/http-blah] %give %done ~)
(ex-boon %0 %ack 'http://client.com/~/eauth')
(ex-wait /eauth/expire/visiting/~hoster/0vnonce (add now ~m5))
==
:: visitor uses eauth page to approve the login attempt,
:: we send ~hoster the token and redirect the visitor
::
;< mos=(list move) bind:m grant
;< now=@da bind:m get-now
;< ~ bind:m
%+ expect-moves mos
=/ loc=@t
'http://hoster.com/~/eauth?nonce=0vnonce&token=0v4.qkgot.d07e3.pi1qd.m1bhj.ti8bo'
:~ (ex-boon %0 %fin 0v4.qkgot.d07e3.pi1qd.m1bhj.ti8bo)
(ex-response 303 ~['location'^loc 'set-cookie'^cookie-string] ~)
==
(pure:m ~)
::
++ test-eauth-unauthenticated-approval
%- eval-mare
=/ m (mare ,~)
^- form:m
=, client:eauth
;< ~ bind:m (setup-for-eauth 'http://client.com')
;< * bind:m start
:: visitor attempts to approve an eauth attempt without being authenticated
::
;< mos=(list move) bind:m
=/ body 'server=~hoster&nonce=0vnonce'
(post '/~/eauth' [g-auth]~ body)
:: eyre must not comply, instead redirect to login page
::
;< ~ bind:m
%+ expect-moves mos
:~ (ex-response 303 ~['location'^'/~/login?redirect=%2F~%2Feauth' g-head] ~)
==
(pure:m ~)
::
++ test-perform-init-start-channel
%- eval-mare
perform-init-start-channel-2
::
++ perform-init-start-channel-2
=/ m (mare ,~)
^- form:m
;< ~ bind:m perform-init-wo-timer
;< ~ bind:m perform-born
;< ~ bind:m (wait ~d1)
;< ~ bind:m perform-authentication-2
:: send the channel a poke and a subscription request
::
;< mos=(list move) bind:m
%^ put '/~/channel/0123456789abcdef' cookie
'''
[{"action": "poke",
"id": 0,
"ship": "nul",
"app": "one",
"mark": "a",
"json": 5},
{"action": "subscribe",
"id": 1,
"ship": "nul",
"app": "two",
"path": "/one/two/three"}
]
'''
;< now=@da bind:m get-now
=/ mov-1
%^ ex-gall-deal /channel/poke/'0123456789abcdef'/'0' ~nul
[%one %poke-as %a %json !>([%n '5'])]
=/ mov-2
%^ ex-gall-deal /channel/subscription/'0123456789abcdef'/'1'/~nul/two
~nul
[%two %watch /one/two/three]
=/ mov-3 (ex-response 204 ['set-cookie' cookie-string]~ ~)
=/ mov-4
%+ ex ~[/http-blah]
[%pass /channel/timeout/'0123456789abcdef' %b %wait (add now ~h12)]
(expect-moves mos mov-1 mov-2 mov-3 mov-4 ~)
--