shrub/pkg/arvo/sys/vane/eyre.hoon

3210 lines
104 KiB
Plaintext
Raw Normal View History

2018-09-20 02:29:36 +03:00
!:
:: lighter than eyre
::
2020-12-06 11:38:37 +03:00
|= our=ship
=, eyre
2018-09-20 02:29:36 +03:00
:: internal data structures
::
=> =~
::
:: internal data structures that won't go in zuse
::
|%
+$ move
::
$: :: duct: request identifier
::
=duct
::
::
2020-12-08 03:47:06 +03:00
card=(wind note gift)
2018-09-20 02:29:36 +03:00
==
:: +note: private request from eyre to another vane
2018-09-20 02:29:36 +03:00
::
+$ note
2018-11-16 00:43:10 +03:00
$% :: %b: to behn
::
$: %b
::
::
$% [%rest p=@da]
[%wait p=@da]
== ==
$: %c
$>(%warp task:clay)
2021-05-27 21:01:06 +03:00
==
2019-03-13 23:29:22 +03:00
:: %d: to dill
::
$: %d
::
::
$% [%flog =flog:dill]
== ==
2019-11-19 07:36:21 +03:00
:: %g: to gall
2018-09-21 02:36:04 +03:00
::
2019-11-19 07:36:21 +03:00
$: %g
2018-09-21 02:36:04 +03:00
::
::
2020-12-08 03:47:06 +03:00
$>(%deal task:gall)
2019-11-05 10:42:59 +03:00
== ==
:: +sign: private response from another vane to eyre
2018-09-24 21:48:19 +03:00
::
+$ sign
2020-12-08 03:22:26 +03:00
$% $: %behn
$% [%wake error=(unit tang)]
== ==
2020-12-08 03:22:26 +03:00
$: %gall
2020-12-08 03:47:06 +03:00
gift:gall
:: $>(%unto gift:gall)
2021-05-20 00:01:05 +03:00
::
==
$: %clay
gift:clay
:: $>(%writ gift:clay)
::
2019-11-05 10:42:59 +03:00
== ==
2018-09-20 02:29:36 +03:00
--
:: more structures
::
|%
++ axle
$: :: date: date at which http-server's state was updated to this data structure
2018-09-20 02:29:36 +03:00
::
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
date=%~2023.5.3
2018-09-20 02:29:36 +03:00
:: server-state: state of inbound requests
::
=server-state
==
:: +server-state: state relating to open inbound HTTP connections
::
+$ server-state
$: :: bindings: actions to dispatch to when a binding matches
::
:: Eyre is responsible for keeping its bindings sorted so that it
:: will trigger on the most specific binding first. Eyre should send
:: back an error response if an already bound binding exists.
::
:: TODO: It would be nice if we had a path trie. We could decompose
:: the :binding into a (map (unit @t) (trie knot =action)).
::
bindings=(list [=binding =duct =action])
2023-03-25 07:18:25 +03:00
:: cache: mapping from url to versioned entry
::
cache=(map url=@t [aeon=@ud val=(unit cache-entry)])
:: cors-registry: state used and managed by the +cors core
::
=cors-registry
2018-09-24 21:48:19 +03:00
:: connections: open http connections not fully complete
2018-09-21 02:36:04 +03:00
::
connections=(map duct outstanding-connection)
2018-09-27 02:18:40 +03:00
:: authentication-state: state managed by the +authentication core
::
=authentication-state
:: channel-state: state managed by the +channel core
::
=channel-state
:: domains: domain-names that resolve to us
::
domains=(set turf)
:: http-config: our server configuration
::
=http-config
:: ports: live servers
::
ports=[insecure=@ud secure=(unit @ud)]
:: outgoing-duct: to unix
::
outgoing-duct=duct
2023-02-08 23:19:14 +03:00
:: verb: verbosity
2023-02-08 22:43:13 +03:00
::
2023-02-08 23:19:14 +03:00
verb=@
2018-09-24 21:48:19 +03:00
==
:: channel-request: an action requested on a channel
::
+$ channel-request
$% :: %ack: acknowledges that the client has received events up to :id
::
[%ack event-id=@ud]
:: %poke: pokes an application, validating :noun against :mark
::
[%poke request-id=@ud ship=@p app=term mark=@tas =noun]
:: %poke-json: pokes an application, translating :json to :mark
::
[%poke-json request-id=@ud ship=@p app=term mark=@tas =json]
2019-11-07 09:19:32 +03:00
:: %watch: subscribes to an application path
::
2019-11-14 06:38:13 +03:00
[%subscribe request-id=@ud ship=@p app=term =path]
2019-11-07 09:19:32 +03:00
:: %leave: unsubscribes from an application path
::
2019-11-14 06:38:13 +03:00
[%unsubscribe request-id=@ud subscription-id=@ud]
:: %delete: kills a channel
::
[%delete ~]
==
:: clog-timeout: the delay between acks after which clog-threshold kicks in
::
++ clog-timeout ~s30
:: clog-threshold: maximum per-subscription event buildup, after clog-timeout
::
++ clog-threshold 50
:: channel-timeout: the delay before a channel should be reaped
::
++ channel-timeout ~h12
:: session-timeout: the delay before an idle session expires
::
++ session-timeout ~d7
2018-09-20 02:29:36 +03:00
--
:: utilities
::
|%
:: +combine-octs: combine multiple octs into one
::
++ combine-octs
|= a=(list octs)
^- octs
:- %+ roll a
|= [=octs sum=@ud]
(add sum p.octs)
(can 3 a)
:: +prune-events: removes all items from the front of the queue up to :id
::
:: also produces, per request-id, the amount of events that have got acked,
:: for use with +subtract-acked-events.
::
++ prune-events
=| acked=(map @ud @ud)
|= [q=(qeu [id=@ud @ud channel-event]) id=@ud]
^+ [acked q]
:: if the queue is now empty, that's fine
::
?: =(~ q)
[acked ~]
::
=/ next=[item=[id=@ud request-id=@ud channel-event] _q] ~(get to q)
:: if the head of the queue is newer than the acknowledged id, we're done
::
?: (gth id.item.next id)
[acked q]
:: otherwise, note the ack, and check next item
::
%_ $
q +:next
::
acked
=, item.next
%+ ~(put by acked) request-id
+((~(gut by acked) request-id 0))
==
:: +subtract-acked-events: update the subscription map's pending ack counts
::
++ subtract-acked-events
|= [acked=(map @ud @ud) unacked=(map @ud @ud)]
^+ unacked
%+ roll ~(tap by acked)
|= [[rid=@ud ack=@ud] unacked=_unacked]
?~ sus=(~(get by unacked) rid)
unacked
%+ ~(put by unacked) rid
?: (lte u.sus ack) 0
(sub u.sus ack)
:: +find-channel-mode: deduce requested mode from headers
::
++ find-channel-mode
|= [met=method:http hes=header-list:http]
^- ?(%json %jam)
=+ ^- [hed=@t jam=@t]
?: ?=(%'GET' met) ['x-channel-format' 'application/x-urb-jam']
['content-type' 'application/x-urb-jam']
=+ typ=(bind (get-header:http hed hes) :(cork trip cass crip))
?:(=(`jam typ) %jam %json)
:: +parse-channel-request: parses a list of channel-requests
::
++ parse-channel-request
|= [mode=?(%json %jam) body=octs]
^- (each (list channel-request) @t)
?- mode
%json
?~ maybe-json=(de-json:html q.body)
|+'put body not json'
?~ maybe-requests=(parse-channel-request-json u.maybe-json)
|+'invalid channel json'
&+u.maybe-requests
::
%jam
?~ maybe-noun=(bind (slaw %uw q.body) cue)
|+'invalid request format'
?~ maybe-reqs=((soft (list channel-request)) u.maybe-noun)
~& [%miss u.maybe-noun]
|+'invalid request data'
&+u.maybe-reqs
==
:: +parse-channel-request-json: parses a json list of channel-requests
::
2018-11-16 00:43:10 +03:00
:: Parses a json array into a list of +channel-request. If any of the items
:: in the list fail to parse, the entire thing fails so we can 400 properly
:: to the client.
::
++ parse-channel-request-json
2018-11-16 00:43:10 +03:00
|= request-list=json
^- (unit (list channel-request))
:: parse top
::
=, dejs-soft:format
=- ((ar -) request-list)
::
|= item=json
^- (unit channel-request)
::
?~ maybe-key=((ot action+so ~) item)
~
?: =('ack' u.maybe-key)
((pe %ack (ot event-id+ni ~)) item)
2018-11-16 00:43:10 +03:00
?: =('poke' u.maybe-key)
%. item
%+ pe %poke-json
(ot id+ni ship+(su fed:ag) app+so mark+(su sym) json+some ~)
2018-11-16 00:43:10 +03:00
?: =('subscribe' u.maybe-key)
%. item
2019-11-14 06:38:13 +03:00
%+ pe %subscribe
(ot id+ni ship+(su fed:ag) app+so path+(su stap) ~)
2018-11-16 00:43:10 +03:00
?: =('unsubscribe' u.maybe-key)
%. item
2019-11-14 06:38:13 +03:00
%+ pe %unsubscribe
(ot id+ni subscription+ni ~)
?: =('delete' u.maybe-key)
`[%delete ~]
2018-11-16 00:43:10 +03:00
:: if we reached this, we have an invalid action key. fail parsing.
::
~
2018-09-27 02:18:40 +03:00
:: +login-page: internal page to login to an Urbit
::
++ login-page
|= [redirect-url=(unit @t) our=@p =identity failed=?]
2018-09-27 02:18:40 +03:00
^- octs
=+ redirect-str=?~(redirect-url "" (trip u.redirect-url))
%- as-octs:mimes:html
%- crip
%- en-xml:html
=/ favicon %+
weld "<svg width='10' height='10' viewBox='0 0 10 10' xmlns='http://www.w3.org/2000/svg'>"
"<circle r='3.09' cx='5' cy='5' /></svg>"
2018-09-27 02:18:40 +03:00
;html
;head
;meta(charset "utf-8");
2020-02-11 03:48:47 +03:00
;meta(name "viewport", content "width=device-width, initial-scale=1, shrink-to-fit=no");
;link(rel "icon", type "image/svg+xml", href (weld "data:image/svg+xml;utf8," favicon));
;title:"Urbit"
;style:'''
@import url("https://rsms.me/inter/inter.css");
@font-face {
font-family: "Source Code Pro";
2020-02-11 03:48:47 +03:00
src: url("https://storage.googleapis.com/media.urbit.org/fonts/scp-regular.woff");
font-weight: 400;
font-display: swap;
}
:root {
--red05: rgba(255,65,54,0.05);
--red100: rgba(255,65,54,1);
--blue05: rgba(33,157,255,0.05);
--blue30: rgba(33,157,255,0.3);
--blue100: rgba(33,157,255,1);
--black05: rgba(0,0,0,0.05);
--black20: rgba(0,0,0,0.2);
--black60: rgba(0,0,0,0.6);
--white: rgba(255,255,255,1);
}
html {
font-family: Inter, sans-serif;
height: 100%;
margin: 0;
width: 100%;
background: var(--white);
color: var(--black100);
2020-02-11 03:48:47 +03:00
-webkit-font-smoothing: antialiased;
line-height: 1.5;
font-size: 12px;
display: flex;
flex-flow: row nowrap;
justify-content: center;
}
body {
display: flex;
flex-flow: column nowrap;
justify-content: center;
max-width: 300px;
padding: 1rem;
2020-02-11 03:48:47 +03:00
width: 100%;
2019-08-09 00:16:45 +03:00
}
body > *,
form > input {
width: 100%;
2020-02-11 03:48:47 +03:00
}
form {
display: flex;
flex-flow: column;
align-items: flex-start;
2020-02-11 03:48:47 +03:00
}
input {
background: transparent;
border: 1px solid var(--black20);
padding: 8px;
border-radius: 4px;
font-size: inherit;
color: var(--black);
box-shadow: none;
2020-02-11 03:48:47 +03:00
}
input:disabled {
background: var(--black05);
color: var(--black60);
2020-02-11 03:48:47 +03:00
}
input:focus {
outline: none;
border-color: var(--blue30);
}
input:invalid:not(:focus) {
background: var(--red05);
border-color: var(--red100);
outline: none;
color: var(--red100);
2020-02-11 03:48:47 +03:00
}
button[type=submit] {
margin-top: 16px;
padding: 8px 16px;
border-radius: 4px;
background: var(--blue100);
color: var(--white);
border: 1px solid var(--blue100);
2020-02-11 03:48:47 +03:00
}
input:invalid ~ button[type=submit] {
border-color: currentColor;
background: var(--blue05);
color: var(--blue30);
pointer-events: none;
2020-02-11 03:48:47 +03:00
}
span.guest {
color: var(--black20);
}
span.failed {
display: flex;
flex-flow: row nowrap;
height: 16px;
align-items: center;
margin-top: 6px;
color: var(--red100);
2020-02-11 03:48:47 +03:00
}
span.failed svg {
height: 12px;
margin-right: 6px;
2020-02-11 03:48:47 +03:00
}
span.failed circle,
span.failed line {
fill: transparent;
stroke: currentColor
}
.mono {
font-family: 'Source Code Pro', monospace;
2020-02-11 03:48:47 +03:00
}
2020-02-22 05:56:25 +03:00
@media all and (prefers-color-scheme: dark) {
:root {
--white: rgb(51, 51, 51);
--black100: rgba(255,255,255,1);
--black05: rgba(255,255,255,0.05);
--black20: rgba(255,255,255,0.2);
2020-02-11 03:48:47 +03:00
}
}
'''
2018-09-27 02:18:40 +03:00
==
;body
;p:"Urbit ID"
;input(value "{(scow %p our)}", disabled "true", class "mono");
;p:"Access Key"
;form(action "/~/login", method "post", enctype "application/x-www-form-urlencoded")
;input
=type "password"
=name "password"
=placeholder "sampel-ticlyt-migfun-falmel"
=class "mono"
=required "true"
=minlength "27"
=maxlength "27"
=pattern "((?:[a-z]\{6}-)\{3}(?:[a-z]\{6}))"
=autofocus "true";
;input(type "hidden", name "redirect", value redirect-str);
;+ ?. failed ;span;
;span.failed
;svg(xmlns "http://www.w3.org/2000/svg", viewBox "0 0 12 12")
;circle(cx "6", cy "6", r "5.5");
;line(x1 "3.27", y1 "3.27", x2 "8.73", y2 "8.73");
;line(x1 "8.73", y1 "3.27", x2 "3.27", y2 "8.73");
2020-02-11 03:48:47 +03:00
==
Key is incorrect
==
;button(type "submit"):"Continue"
2018-09-27 02:18:40 +03:00
==
;* ?. ?=(%fake -.identity) ~
=+ id=(trim 29 (scow %p who.identity))
;+ ;span.guest.mono
; Current guest identity:
;br;
; {p.id}
;br;
; {q.id}
==
2018-09-27 02:18:40 +03:00
==
;script:'''
var failSpan = document.querySelector('.failed');
if (failSpan) {
document.querySelector("input[type=password]")
.addEventListener('keyup', function (event) {
failSpan.style.display = 'none';
});
}
'''
2018-09-27 02:18:40 +03:00
==
:: +render-tang-to-marl: renders a tang and adds <br/> tags between each line
2018-10-11 01:26:51 +03:00
::
++ render-tang-to-marl
|= [wid=@u tan=tang]
2018-10-11 01:26:51 +03:00
^- marl
=/ raw=(list tape) (zing (turn tan |=(a=tank (wash 0^wid a))))
2018-10-11 01:26:51 +03:00
::
|- ^- marl
?~ raw ~
[;/(i.raw) ;br; $(raw t.raw)]
:: +render-tang-to-wall: renders tang as text lines
::
++ render-tang-to-wall
|= [wid=@u tan=tang]
^- wall
(zing (turn tan |=(a=tank (wash 0^wid a))))
:: +wall-to-octs: text to binary output
::
++ wall-to-octs
|= =wall
^- (unit octs)
::
?: =(~ wall)
~
::
:- ~
%- as-octs:mimes:html
%- crip
%- zing ^- ^wall
%- zing ^- (list ^wall)
%+ turn wall
|= t=tape
^- ^wall
~[t "\0a"]
:: +internal-server-error: 500 page, with a tang
::
++ internal-server-error
2018-10-11 01:26:51 +03:00
|= [authorized=? url=@t t=tang]
^- octs
%- as-octs:mimes:html
%- crip
%- en-xml:html
;html
;head
;title:"500 Internal Server Error"
==
;body
;h1:"Internal Server Error"
;p:"There was an error while handling the request for {(trip url)}."
;* ?: authorized
;=
;code:"*{(render-tang-to-marl 80 t)}"
==
~
==
==
:: +error-page: error page, with an error string if logged in
::
++ error-page
|= [code=@ud authorized=? url=@t t=tape]
^- octs
::
=/ code-as-tape=tape (format-ud-as-integer code)
=/ message=tape
?+ code "{(scow %ud code)} Error"
2020-06-12 03:13:13 +03:00
%400 "Bad Request"
%403 "Forbidden"
%404 "Not Found"
%405 "Method Not Allowed"
%500 "Internal Server Error"
==
::
%- as-octs:mimes:html
%- crip
%- en-xml:html
;html
;head
;title:"{code-as-tape} {message}"
==
;body
;h1:"{message}"
;p:"There was an error while handling the request for {(trip url)}."
;* ?: authorized
;=
;code:"{t}"
==
~
==
==
:: +format-ud-as-integer: prints a number for consumption outside urbit
::
++ format-ud-as-integer
|= a=@ud
^- tape
?: =(0 a) ['0' ~]
%- flop
|- ^- tape
?:(=(0 a) ~ [(add '0' (mod a 10)) $(a (div a 10))])
:: +host-matches: %.y if the site :binding should be used to handle :host
::
++ host-matches
|= [binding=(unit @t) host=(unit @t)]
^- ?
:: if the binding allows for matching anything, match
::
?~ binding
%.y
:: if the host is ~, that means we're trying to bind nothing to a real
:: binding. fail.
::
?~ host
%.n
:: otherwise, do a straight comparison
::
=(u.binding u.host)
:: +find-suffix: returns [~ /tail] if :full is (weld :prefix /tail)
::
++ find-suffix
|= [prefix=path full=path]
^- (unit path)
?~ prefix
`full
?~ full
~
?. =(i.prefix i.full)
~
$(prefix t.prefix, full t.full)
2018-10-04 00:05:36 +03:00
:: +simplified-url-parser: returns [(each @if @t) (unit port=@ud)]
::
++ simplified-url-parser
;~ plug
;~ pose
%+ stag %ip
=+ tod=(ape:ag ted:ab)
%+ bass 256
;~(plug tod (stun [3 3] ;~(pfix dot tod)))
::
(stag %site (cook crip (star ;~(pose dot alp))))
==
;~ pose
(stag ~ ;~(pfix col dim:ag))
(easy ~)
==
==
2022-09-24 12:52:34 +03:00
:: +host-sans-port: strip the :<port> from a host string
::
++ host-sans-port
;~ sfix
%+ cook crip
%- star
;~ less
;~(plug col (punt dem) ;~(less next (easy ~)))
next
==
(star next)
==
:: +per-server-event: per-event server core
2018-09-20 02:29:36 +03:00
::
++ per-server-event
~% %eyre-per-server-event ..part ~
:: gate that produces the +per-server-event core from event information
::
2020-12-06 13:55:19 +03:00
|= [[eny=@ =duct now=@da rof=roof] state=server-state]
2019-11-14 00:39:29 +03:00
=/ eyre-id (scot %ta (cat 3 'eyre_' (scot %uv (sham duct))))
2018-09-20 02:29:36 +03:00
|%
:: +request-local: bypass authentication for local lens connections
::
++ request-local
|= [secure=? =address =request:http]
^- [(list move) server-state]
::
=/ act [%app app=%lens]
::
=/ connection=outstanding-connection
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
[act [& secure address request] [*@uv [%ours ~]] ~ 0]
::
=. connections.state
2023-02-09 01:03:02 +03:00
%. (~(put by connections.state) duct connection)
2023-02-09 20:55:08 +03:00
(trace 2 |.("{<duct>} creating local"))
::
:_ state
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
(subscribe-to-app [%ours ~] app.act inbound-request.connection)
2018-09-21 02:36:04 +03:00
:: +request: starts handling an inbound http request
::
2018-09-20 02:29:36 +03:00
++ request
|= [secure=? =address =request:http]
2018-09-20 02:29:36 +03:00
^- [(list move) server-state]
=* headers header-list.request
:: for requests from localhost, respect the "forwarded" header
::
=/ [secure=? =^address]
=* same [secure address]
?. =([%ipv4 .127.0.0.1] address) same
?~ forwards=(forwarded-params headers) same
:- (fall (forwarded-secure u.forwards) secure)
(fall (forwarded-for u.forwards) address)
2018-09-20 02:29:36 +03:00
::
=/ host (get-header:http 'host' headers)
=/ [=action suburl=@t]
(get-action-for-binding host url.request)
::
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
=^ [suv=@uv =identity som=(list move)] state
(session-for-request:authentication request)
=; [moz=(list move) sat=server-state]
[(weld som moz) sat]
::
=/ authenticated ?=(%ours -.identity)
2018-09-21 02:36:04 +03:00
:: record that we started an asynchronous response
::
=/ connection=outstanding-connection
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
[action [authenticated secure address request] [suv identity] ~ 0]
=. connections.state
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
:: NB: required by +handle-response and +handle-request:authentication.
:: XX optimize
::
(~(put by connections.state) duct connection)
:: redirect to https if insecure, redirects enabled
:: and secure port live
::
?: ?& !secure
redirect.http-config.state
?=(^ secure.ports.state)
==
=/ location=@t
%+ rap 3
:~ 'https://'
(rash (fall host '') host-sans-port)
?: =(443 u.secure.ports.state)
''
(crip ":{(a-co:co u.secure.ports.state)}")
?: ?=([[~ ~] ~] (parse-request-line url.request))
'/'
url.request
==
%- handle-response
:* %start
:- status-code=301
headers=['location' location]~
data=~
complete=%.y
==
:: figure out whether this is a cors request,
:: whether the origin is approved or not,
:: and maybe add it to the "pending approval" set
::
=/ origin=(unit origin)
(get-header:http 'origin' headers)
=^ cors-approved requests.cors-registry.state
=, cors-registry.state
?~ origin [| requests]
?: (~(has in approved) u.origin) [& requests]
?: (~(has in rejected) u.origin) [| requests]
[| (~(put in requests) u.origin)]
:: if this is a cors preflight request from an approved origin
:: handle it synchronously
::
?: &(?=(^ origin) cors-approved ?=(%'OPTIONS' method.request))
%- handle-response
=; =header-list:http
[%start [204 header-list] ~ &]
:: allow the method and headers that were asked for,
:: falling back to wildcard if none specified
::
::NOTE +handle-response will add the rest of the headers
::
:~ :- 'Access-Control-Allow-Methods'
=- (fall - '*')
(get-header:http 'access-control-request-method' headers)
::
:- 'Access-Control-Allow-Headers'
=- (fall - '*')
(get-header:http 'access-control-request-headers' headers)
==
2023-03-25 07:18:25 +03:00
:: handle requests to the cache
::
=/ entry (~(get by cache.state) url.request)
?: &(?=(^ entry) ?=(%'GET' method.request))
(handle-cache-req authenticated request val.u.entry)
::
?- -.action
2018-09-20 02:29:36 +03:00
%gen
=/ bek=beak [our desk.generator.action da+now]
=/ sup=spur path.generator.action
2020-12-06 13:55:19 +03:00
=/ ski (rof ~ %ca bek sup)
2020-05-08 08:29:25 +03:00
=/ cag=cage (need (need ski))
?> =(%vase p.cag)
=/ gat=vase !<(vase q.cag)
=/ res=toon
2020-12-06 13:55:19 +03:00
%- mock :_ (look rof ~)
:_ [%9 2 %0 1] |.
%+ slam
%+ slam gat
!>([[now=now eny=eny bek=bek] ~ ~])
!>([authenticated request])
?: ?=(%2 -.res)
=+ connection=(~(got by connections.state) duct)
%^ return-static-data-on-duct 500 'text/html'
%: internal-server-error
authenticated.inbound-request.connection
url.request.inbound-request.connection
leaf+"generator crashed"
p.res
==
?: ?=(%1 -.res)
=+ connection=(~(got by connections.state) duct)
%^ return-static-data-on-duct 500 'text/html'
%: internal-server-error
authenticated.inbound-request.connection
url.request.inbound-request.connection
leaf+"scry blocked on"
2020-12-06 13:55:19 +03:00
(fall (bind (bind ((soft path) p.res) smyt) (late ~)) ~)
==
=/ result ;;(simple-payload:http +.p.res)
:: ensure we have a valid content-length header
2018-09-21 02:36:04 +03:00
::
:: We pass on the response and the headers the generator produces, but
:: ensure that we have a single content-length header set correctly in
:: the returned if this has a body, and has no content-length if there
:: is no body returned to the client.
::
=. headers.response-header.result
?~ data.result
(delete-header:http 'content-length' headers.response-header.result)
2018-10-11 01:26:51 +03:00
::
%^ set-header:http 'content-length'
(crip (format-ud-as-integer p.u.data.result))
headers.response-header.result
::
%- handle-response
^- http-event:http
:* %start
response-header.result
data.result
complete=%.y
==
2018-09-20 02:29:36 +03:00
::
%app
2018-09-21 02:36:04 +03:00
:_ state
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
(subscribe-to-app identity app.action inbound-request.connection)
2018-09-27 02:18:40 +03:00
::
%authentication
(handle-request:authentication secure address [suv identity] request)
::
%logout
(handle-logout:authentication [suv identity] request)
2018-11-13 22:12:59 +03:00
::
%channel
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
(handle-request:by-channel [suv identity] address request)
::
%scry
(handle-scry authenticated address request(url suburl))
2022-08-01 01:50:36 +03:00
::
%name
(handle-name identity request)
::
%host
%^ return-static-data-on-duct 200 'text/plain'
(as-octs:mimes:html (scot %p our))
::
%four-oh-four
%^ return-static-data-on-duct 404 'text/html'
(error-page 404 authenticated url.request ~)
2018-09-27 02:18:40 +03:00
==
:: +handle-name: respond with the requester's @p
2022-08-01 01:50:36 +03:00
::
++ handle-name
|= [=identity =request:http]
^- (quip move server-state)
2022-08-01 01:50:36 +03:00
?. =(%'GET' method.request)
%^ return-static-data-on-duct 405 'text/html'
(error-page 405 & url.request "may only GET name")
2022-08-01 01:50:36 +03:00
%^ return-static-data-on-duct 200 'text/plain'
=; nom=@p (as-octs:mimes:html (scot %p nom))
?- -.identity
%ours our
%fake who.identity
==
2023-03-25 07:18:25 +03:00
:: +handle-cache-req: respond with cached value, 404 or 500
::
++ handle-cache-req
|= [authenticated=? =request:http entry=(unit cache-entry)]
|^ ^- (quip move server-state)
?~ entry
(error-response 404 "cache entry for that binding was deleted")
?: &(auth.u.entry !authenticated)
(error-response 403 ~)
=* body body.u.entry
?- -.body
%payload
%- handle-response
:* %start
response-header.simple-payload.body
data.simple-payload.body
complete=%.y
==
==
::
++ error-response
|= [status=@ud =tape]
^- (quip move server-state)
%^ return-static-data-on-duct status 'text/html'
(error-page status authenticated url.request tape)
--
:: +handle-scry: respond with scry result, 404 or 500
::
++ handle-scry
|= [authenticated=? =address =request:http]
|^ ^- (quip move server-state)
?. authenticated
2022-08-11 19:30:37 +03:00
(error-response 403 ~)
?. =(%'GET' method.request)
2022-08-11 19:30:37 +03:00
(error-response 405 "may only GET scries")
:: make sure the path contains an app to scry into
::
=+ req=(parse-request-line url.request)
?. ?=(^ site.req)
2022-08-11 19:30:37 +03:00
(error-response 400 "scry path must start with app name")
:: attempt the scry that was asked for
::
=/ res=(unit (unit cage))
(do-scry %gx i.site.req (snoc t.site.req (fall ext.req %mime)))
2022-08-11 19:30:37 +03:00
?~ res (error-response 500 "failed scry")
?~ u.res (error-response 404 "no scry result")
=* mark p.u.u.res
=* vase q.u.u.res
:: attempt to find conversion gate to mime
::
=/ tub=(unit tube:clay)
(find-tube i.site.req mark %mime)
2022-08-11 19:30:37 +03:00
?~ tub (error-response 500 "no tube from {(trip mark)} to mime")
:: attempt conversion, then send results
::
=/ mym=(each mime tang)
(mule |.(!<(mime (u.tub vase))))
?- -.mym
2022-08-11 19:30:37 +03:00
%| (error-response 500 "failed tube from {(trip mark)} to mime")
%& %+ return-static-data-on-duct 200
[(rsh 3 (spat p.p.mym)) q.p.mym]
==
::
++ find-tube
|= [dap=term from=mark to=mark]
^- (unit tube:clay)
?: =(from to) `(bake same vase)
=/ des=(unit (unit cage))
(do-scry %gd dap /$)
?. ?=([~ ~ *] des) ~
=+ !<(=desk q.u.u.des)
=/ tub=(unit (unit cage))
(do-scry %cc desk /[from]/[to])
?. ?=([~ ~ %tube *] tub) ~
`!<(tube:clay q.u.u.tub)
::
++ do-scry
|= [care=term =desk =path]
^- (unit (unit cage))
2020-12-06 13:55:19 +03:00
(rof ~ care [our desk da+now] path)
::
2022-08-11 19:30:37 +03:00
++ error-response
|= [status=@ud =tape]
^- (quip move server-state)
%^ return-static-data-on-duct status 'text/html'
(error-page status authenticated url.request tape)
--
:: +subscribe-to-app: subscribe to app and poke it with request data
::
++ subscribe-to-app
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
|= [=identity app=term =inbound-request:eyre]
^- (list move)
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
:~ %+ deal-as
/watch-response/[eyre-id]
[identity our app %watch /http-response/[eyre-id]]
::
%+ deal-as
/run-app-request/[eyre-id]
:^ identity our app
:+ %poke %handle-http-request
!>(`[@ta inbound-request:eyre]`[eyre-id inbound-request])
==
2018-10-22 21:36:30 +03:00
:: +cancel-request: handles a request being externally aborted
::
++ cancel-request
^- [(list move) server-state]
::
?~ connection=(~(get by connections.state) duct)
:: nothing has handled this connection
::
[~ state]
::
2018-10-24 01:08:17 +03:00
=. connections.state (~(del by connections.state) duct)
::
?- -.action.u.connection
%gen [~ state]
%app
:_ state
:_ ~
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
=, u.connection
%- (trace 1 |.("leaving subscription to {<app.action>}"))
(deal-as /watch-response/[eyre-id] identity our app.action %leave ~)
::
?(%authentication %logout)
[~ state]
2018-11-13 22:12:59 +03:00
::
%channel
2018-11-22 00:37:26 +03:00
on-cancel-request:by-channel
::
?(%scry %four-oh-four %name %host)
:: it should be impossible for these to be asynchronous
::
!!
==
2018-09-27 02:18:40 +03:00
:: +return-static-data-on-duct: returns one piece of data all at once
::
++ return-static-data-on-duct
|= [code=@ content-type=@t data=octs]
^- [(list move) server-state]
::
%- handle-response
:* %start
:- status-code=code
2018-09-27 02:18:40 +03:00
^= headers
:~ ['content-type' content-type]
['content-length' (crip (format-ud-as-integer p.data))]
2018-09-27 02:18:40 +03:00
==
data=[~ data]
complete=%.y
2018-09-20 02:29:36 +03:00
==
2018-09-27 02:18:40 +03:00
:: +authentication: per-event authentication as this Urbit's owner
::
:: Right now this hard codes the authentication page using the old +code
:: system, but in the future should be pluggable so we can use U2F or
:: WebAuthn or whatever is more secure than passwords.
::
++ authentication
|%
:: +handle-request: handles an http request for the login page
2018-09-27 02:18:40 +03:00
::
++ handle-request
|= [secure=? =address [session-id=@uv =identity] =request:http]
2018-09-27 02:18:40 +03:00
^- [(list move) server-state]
::
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
:: if we received a simple get: redirect if logged in, otherwise
:: show login page
2018-09-27 02:18:40 +03:00
::
?: =('GET' method.request)
2018-09-27 02:18:40 +03:00
:: parse the arguments out of request uri
::
=+ request-line=(parse-request-line url.request)
=/ redirect (get-header:http 'redirect' args.request-line)
?. (request-is-logged-in request)
%^ return-static-data-on-duct 200 'text/html'
(login-page redirect our identity %.n)
=/ session-id (session-id-from-request request)
:: session-id should always be populated here since we are logged in
?~ session-id
%^ return-static-data-on-duct 200 'text/html'
(login-page redirect our identity %.n)
=/ cookie-line=@t
(session-cookie-string u.session-id &)
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
=/ actual-redirect
?~ redirect '/'
?:(=(u.redirect '') '/' u.redirect)
%- handle-response
:* %start
:- status-code=303
^= headers
:~ ['location' actual-redirect]
['set-cookie' cookie-line]
==
data=~
complete=%.y
==
2018-09-27 02:18:40 +03:00
:: if we are not a post, return an error
::
?. =('POST' method.request)
%^ return-static-data-on-duct 400 'text/html'
(login-page ~ our identity %.n)
2018-09-27 02:18:40 +03:00
:: we are a post, and must process the body type as form data
::
?~ body.request
%^ return-static-data-on-duct 400 'text/html'
(login-page ~ our identity %.n)
2018-09-27 02:18:40 +03:00
::
=/ parsed=(unit (list [key=@t value=@t]))
(rush q.u.body.request yquy:de-purl:html)
2018-09-27 02:18:40 +03:00
?~ parsed
%^ return-static-data-on-duct 400 'text/html'
(login-page ~ our identity %.n)
2018-09-27 02:18:40 +03:00
::
=/ redirect=(unit @t) (get-header:http 'redirect' u.parsed)
?~ password=(get-header:http 'password' u.parsed)
%^ return-static-data-on-duct 400 'text/html'
(login-page redirect our identity %.n)
2018-09-27 02:18:40 +03:00
:: check that the password is correct
::
?. =(u.password code)
%^ return-static-data-on-duct 400 'text/html'
(login-page redirect our identity %.y)
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
:: clean up the session they're changing out from
2018-09-27 02:18:40 +03:00
::
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
=^ moz state
(close-session session-id |)
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
:: initialize the new session
2018-09-27 02:18:40 +03:00
::
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
=^ fex state (start-session %local)
:: associate the new session with the request that caused the login
2018-09-27 02:18:40 +03:00
::
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
:: if we don't do this here, +handle-response will include the old
:: session's cookie, confusing the client.
2018-09-27 02:18:40 +03:00
::
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
=. connections.state
%+ ~(jab by connections.state) duct
|= o=outstanding-connection
o(session-id session.fex)
2018-09-27 02:18:40 +03:00
::
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
=; out=[moves=(list move) server-state]
out(moves [give-session-tokens :(weld moz moves.fex moves.out)])
::NOTE that we don't provide a 'set-cookie' header here.
:: +handle-response does that for us.
?~ redirect
(handle-response %start 204^~ ~ &)
=/ actual-redirect ?:(=(u.redirect '') '/' u.redirect)
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
(handle-response %start 303^['location' actual-redirect]~ ~ &)
:: +handle-logout: handles an http request for logging out
2018-09-27 02:18:40 +03:00
::
++ handle-logout
|= [[session-id=@uv =identity] =request:http]
^- [(list move) server-state]
:: whatever we end up doing, we always respond with a redirect
::
=/ response=$>(%start http-event:http)
=/ redirect=(unit @t)
%+ get-header:http 'redirect'
args:(parse-request-line url.request)
:* %start
response-header=[303 ['location' (fall redirect '/~/login')]~]
data=~
complete=%.y
==
:: read options from the body
:: all: log out all sessions with this identity?
:: sid: which session do we log out? (defaults to requester's)
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
::
=/ arg=header-list:http
?~ body.request ~
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
(fall (rush q.u.body.request yquy:de-purl:html) ~)
=/ all=?
?=(^ (get-header:http 'all' arg))
=/ sid=(unit @uv)
?. ?=(%ours -.identity) `session-id
?~ sid=(get-header:http 'sid' arg) `session-id
:: if you provided the parameter, but it doesn't parse, we just
:: no-op. otherwise, a poorly-implemented frontend might result in
:: accidental log-outs, which would be very annoying.
::
(slaw %uv u.sid)
?~ sid
(handle-response response)
:: if the requester is logging themselves out, make them drop the cookie
::
=? headers.response-header.response =(u.sid session-id)
:_ headers.response-header.response
['set-cookie' (session-cookie-string session-id |)]
:: close the session as requested, then send the response
::
=^ moz1 state (close-session u.sid all)
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
=^ moz2 state (handle-response response)
[[give-session-tokens (weld moz1 moz2)] state]
:: +session-id-from-request: attempt to find a session cookie
2018-09-27 02:18:40 +03:00
::
++ session-id-from-request
|= =request:http
^- (unit @uv)
:: are there cookies passed with this request?
::
:: TODO: In HTTP2, the client is allowed to put multiple 'Cookie'
:: headers.
::
?~ cookie-header=(get-header:http 'cookie' header-list.request)
~
:: is the cookie line is valid?
::
?~ cookies=(rush u.cookie-header cock:de-purl:html)
~
:: is there an urbauth cookie?
::
?~ urbauth=(get-header:http (crip "urbauth-{(scow %p our)}") u.cookies)
~
:: if it's formatted like a valid session cookie, produce it
::
`(unit @)`(rush u.urbauth ;~(pfix (jest '0v') viz:ag))
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
:: +request-is-logged-in: checks to see if the request has non-guest id
::
++ request-is-logged-in
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
|= =request:http
^- ?
?~ session-id=(session-id-from-request request)
|
?~ session=(~(get by sessions.authentication-state.state) u.session-id)
|
&(!?=(%fake -.identity.u.session) (lte now expiry-time.u.session))
:: +request-is-authenticated: checks to see if the request is "us"
::
:: We are considered authenticated if this request has an urbauth
:: Cookie for the local identity that is not expired.
::
++ request-is-authenticated
|= =request:http
^- ?
:: does the request pass a session cookie?
::
?~ session-id=(session-id-from-request request)
%.n
:: is this a session that we know about?
::
?~ session=(~(get by sessions.authentication-state.state) `@uv`u.session-id)
%.n
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
:: does this session have our id, and is it still valid?
::
&(?=(%ours -.identity.u.session) (lte now expiry-time.u.session))
:: +start-session: create a new session with %local or %guest identity
::
++ start-session
|= kind=?(%local %guest)
^- [[session=@uv =identity moves=(list move)] server-state]
=; [key=@uv sid=identity]
:- :+ key sid
:: if no session existed previously, we must kick off the
:: session expiry timer
::
?^ sessions.authentication-state.state ~
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
[duct %pass /sessions/expire %b %wait (add now session-timeout)]~
=- state(sessions.authentication-state -)
%+ ~(put by sessions.authentication-state.state) key
[sid (add now session-timeout) ~]
:: create a new session with a fake identity
::
=/ sik=@uv new-session-key
:- sik
?: ?=(%local kind) [%ours ~]
:- %fake
:: pre-scramble our ship name into its displayed value, and
:: truncate it to be at most moon-length, so that we can overlay
:: it onto the end of a comet name for visual consistency.
:: to prevent escalation, make sure the guest identity isn't ours.
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
::
|-
=; nom=@p
?. =(our nom) nom
$(eny (shas %next-name eny))
%+ end 3^16
%^ cat 3
(end 3^8 (fein:ob our))
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
(~(raw og (shas %fake-name eny)) 128)
:: +session-for-request: get the session details for the request
::
:: creates a guest session if the request does not have a valid session.
:: there is no need to call +give-session-tokens after this, because
:: guest session do not make valid "auth session" tokens.
::
++ session-for-request
|= =request:http
^- [[session=@uv =identity moves=(list move)] server-state]
=* new (start-session %guest)
?~ sid=(session-id-from-request request)
new
?~ ses=(~(get by sessions.authentication-state.state) u.sid)
new
?: (gth now expiry-time.u.ses)
new
[[u.sid identity.u.ses ~] state]
:: +close-session: delete a session and its associated channels
::
:: if :all is true, deletes all sessions that share the same identity.
:: if this closes an %ours session, the caller is responsible for
:: also calling +give-session-tokens afterwards.
::
++ close-session
|= [session-id=@uv all=?]
^- [(list move) server-state]
?~ ses=(~(get by sessions.authentication-state.state) session-id)
[~ state]
:: delete the session(s) and find the associated channels
::
=^ channels=(list @t) sessions.authentication-state.state
=* sessions sessions.authentication-state.state
:: either delete just the specific session and its channels,
::
?. all
:- ~(tap in channels.u.ses)
(~(del by sessions) session-id)
:: or delete all sessions with the identity from :session-id
::
:- %~ tap in
%+ roll ~(val by sessions)
|= [session all=(set @t)]
?. =(identity identity.u.ses) all
(~(uni in all) channels)
%- my
%+ skip ~(tap by sessions)
|= [@uv session]
=(identity identity.u.ses)
:: close all affected channels, then send the response
2018-09-27 02:18:40 +03:00
::
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
=| moves=(list move)
|- ^- (quip move server-state)
?~ channels [moves state]
%- %+ trace 1
|.("{(trip i.channels)} discarding channel due to closed session")
=^ moz state
(discard-channel:by-channel i.channels |)
$(moves (weld moves moz), channels t.channels)
2018-09-27 02:18:40 +03:00
:: +code: returns the same as |code
::
++ code
^- @ta
=/ res=(unit (unit cage))
2020-12-06 13:55:19 +03:00
(rof ~ %j [our %code da+now] /(scot %p our))
(rsh 3 (scot %p ;;(@ q.q:(need (need res)))))
:: +session-cookie-string: compose session cookie
::
++ session-cookie-string
|= [session=@uv extend=?]
^- @t
%- crip
=; max-age=tape
"urbauth-{(scow %p our)}={(scow %uv session)}; Path=/; Max-Age={max-age}"
%- format-ud-as-integer
?. extend 0
(div (msec:milly session-timeout) 1.000)
2018-09-27 02:18:40 +03:00
--
:: +channel: per-event handling of requests to the channel system
::
:: Eyre offers a remote interface to your Urbit through channels, which
:: are persistent connections on the server which can be disconnected and
:: reconnected on the client.
::
++ by-channel
:: moves: the moves to be sent out at the end of this event, reversed
::
=| moves=(list move)
|%
:: +handle-request: handles an http request for the subscription system
::
++ handle-request
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
|= [[session-id=@uv =identity] =address =request:http]
^- [(list move) server-state]
:: parse out the path key the subscription is on
::
=+ request-line=(parse-request-line url.request)
?. ?=([@t @t @t ~] site.request-line)
:: url is not of the form '/~/channel/'
::
%^ return-static-data-on-duct 400 'text/html'
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
(error-page 400 & url.request "malformed channel url")
:: channel-id: unique channel id parsed out of url
::
=+ channel-id=i.t.t.site.request-line
::
?: =('PUT' method.request)
:: PUT methods starts/modifies a channel, and returns a result immediately
::
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
(on-put-request channel-id identity request)
::
?: =('GET' method.request)
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
(on-get-request channel-id [session-id identity] request)
?: =('POST' method.request)
:: POST methods are used solely for deleting channels
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
(on-put-request channel-id identity request)
::
2023-02-09 22:51:34 +03:00
((trace 0 |.("session not a put")) `state)
2018-11-22 00:37:26 +03:00
:: +on-cancel-request: cancels an ongoing subscription
::
:: One of our long lived sessions just got closed. We put the associated
:: session back into the waiting state.
::
++ on-cancel-request
^- [(list move) server-state]
:: lookup the session id by duct
::
2023-02-14 19:46:07 +03:00
%- (trace 1 |.("{<duct>} moving channel to waiting state"))
2018-11-22 00:37:26 +03:00
::
2023-02-09 21:58:24 +03:00
?~ maybe-channel-id=(~(get by duct-to-key.channel-state.state) duct)
2023-02-14 19:46:07 +03:00
((trace 0 |.("{<duct>} no channel to move")) `state)
::
=/ maybe-session
(~(get by session.channel-state.state) u.maybe-channel-id)
2023-02-09 21:58:24 +03:00
?~ maybe-session
((trace 1 |.("{<maybe-session>} session doesn't exist")) `state)
::
=/ heartbeat-cancel=(list move)
?~ heartbeat.u.maybe-session ~
:~ %^ cancel-heartbeat-move
u.maybe-channel-id
date.u.heartbeat.u.maybe-session
duct.u.heartbeat.u.maybe-session
==
::
2018-11-22 00:37:26 +03:00
=/ expiration-time=@da (add now channel-timeout)
::
:- %+ weld heartbeat-cancel
[(set-timeout-move u.maybe-channel-id expiration-time) moves]
2018-11-22 00:37:26 +03:00
%_ state
session.channel-state
%+ ~(jab by session.channel-state.state) u.maybe-channel-id
|= =channel
:: if we are canceling a known channel, it should have a listener
::
?> ?=([%| *] state.channel)
channel(state [%& [expiration-time duct]], heartbeat ~)
2018-11-22 00:37:26 +03:00
::
duct-to-key.channel-state
(~(del by duct-to-key.channel-state.state) duct)
==
:: +set-timeout-timer-for: sets a timeout timer on a channel
::
:: This creates a channel if it doesn't exist, cancels existing timers
:: if they're already set (we cannot have duplicate timers), and (if
:: necessary) moves channels from the listening state to the expiration
:: state.
::
++ update-timeout-timer-for
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
|= [mode=?(%json %jam) =identity channel-id=@t]
2018-11-22 00:37:26 +03:00
^+ ..update-timeout-timer-for
:: when our callback should fire
::
=/ expiration-time=@da (add now channel-timeout)
:: if the channel doesn't exist, create it and set a timer
::
?~ maybe-channel=(~(get by session.channel-state.state) channel-id)
::
%_ ..update-timeout-timer-for
session.channel-state.state
%+ ~(put by session.channel-state.state) channel-id
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
[mode identity [%& expiration-time duct] 0 now ~ ~ ~ ~]
2018-11-22 00:37:26 +03:00
::
moves
[(set-timeout-move channel-id expiration-time) moves]
==
:: if the channel has an active listener, we aren't setting any timers
::
?: ?=([%| *] state.u.maybe-channel)
..update-timeout-timer-for
:: we have a previous timer; cancel the old one and set the new one
::
%_ ..update-timeout-timer-for
session.channel-state.state
%+ ~(jab by session.channel-state.state) channel-id
|= =channel
channel(state [%& [expiration-time duct]])
::
moves
:* (cancel-timeout-move channel-id p.state.u.maybe-channel)
(set-timeout-move channel-id expiration-time)
moves
==
==
::
++ set-heartbeat-move
|= [channel-id=@t heartbeat-time=@da]
^- move
:^ duct %pass /channel/heartbeat/[channel-id]
[%b %wait heartbeat-time]
::
++ cancel-heartbeat-move
|= [channel-id=@t heartbeat-time=@da =^duct]
^- move
:^ duct %pass /channel/heartbeat/[channel-id]
[%b %rest heartbeat-time]
::
2018-11-22 00:37:26 +03:00
++ set-timeout-move
|= [channel-id=@t expiration-time=@da]
^- move
[duct %pass /channel/timeout/[channel-id] %b %wait expiration-time]
::
++ cancel-timeout-move
|= [channel-id=@t expiration-time=@da =^duct]
^- move
:^ duct %pass /channel/timeout/[channel-id]
[%b %rest expiration-time]
:: +on-get-request: handles a GET request
::
:: GET requests open a channel for the server to send events to the
:: client in text/event-stream format.
::
++ on-get-request
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
|= [channel-id=@t [session-id=@uv =identity] =request:http]
^- [(list move) server-state]
:: if there's no channel-id, we must 404
::TODO but arm description says otherwise?
::
?~ maybe-channel=(~(get by session.channel-state.state) channel-id)
%^ return-static-data-on-duct 404 'text/html'
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
(error-page 404 | url.request ~)
:: find the channel creator's identity, make sure it matches
::
?. =(identity identity.u.maybe-channel)
%^ return-static-data-on-duct 403 'text/html'
(error-page 403 | url.request ~)
:: find the requested "mode" and make sure it doesn't conflict
::
=/ mode=?(%json %jam)
(find-channel-mode %'GET' header-list.request)
?. =(mode mode.u.maybe-channel)
%^ return-static-data-on-duct 406 'text/html'
=; msg=tape (error-page 406 %.y url.request msg)
"channel already established in {(trip mode.u.maybe-channel)} mode"
:: when opening an event-stream, we must cancel our timeout timer
:: if there's no duct already bound. Else, kill the old request
:: and replace it
::
=^ cancel-moves state
2021-06-25 01:28:06 +03:00
?. ?=([%| *] state.u.maybe-channel)
:_ state
(cancel-timeout-move channel-id p.state.u.maybe-channel)^~
2021-06-25 01:28:06 +03:00
=/ cancel-heartbeat
?~ heartbeat.u.maybe-channel ~
:_ ~
2021-06-25 01:28:06 +03:00
%+ cancel-heartbeat-move channel-id
[date duct]:u.heartbeat.u.maybe-channel
=- [(weld cancel-heartbeat -<) ->]
2021-06-25 01:28:06 +03:00
(handle-response(duct p.state.u.maybe-channel) [%cancel ~])
:: the request may include a 'Last-Event-Id' header
::
=/ maybe-last-event-id=(unit @ud)
?~ maybe-raw-header=(get-header:http 'last-event-id' header-list.request)
~
(rush u.maybe-raw-header dum:ag)
:: flush events older than the passed in 'Last-Event-ID'
::
=? state ?=(^ maybe-last-event-id)
(acknowledge-events channel-id u.maybe-last-event-id)
:: combine the remaining queued events to send to the client
::
=/ event-replay=wall
%- zing
%- flop
=/ queue events.u.maybe-channel
=| events=(list wall)
|-
^+ events
?: =(~ queue)
events
=^ head queue ~(get to queue)
=, p.head
::NOTE these will only fail if the mark and/or json types changed,
:: since conversion failure also gets caught during first receive.
:: we can't do anything about this, so consider it unsupported.
=/ said
(channel-event-to-tape u.maybe-channel request-id channel-event)
?~ said $
$(events [(event-tape-to-wall id +.u.said) events])
:: send the start event to the client
::
=^ http-moves state
%- handle-response
:* %start
:- 200
:~ ['content-type' 'text/event-stream']
['cache-control' 'no-cache']
['connection' 'keep-alive']
==
(wall-to-octs event-replay)
complete=%.n
==
2018-11-22 00:37:26 +03:00
:: associate this duct with this session key
::
=. duct-to-key.channel-state.state
(~(put by duct-to-key.channel-state.state) duct channel-id)
:: associate this channel with the session cookie
::
=. sessions.authentication-state.state
%+ ~(jab by sessions.authentication-state.state)
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
session-id
|= =session
session(channels (~(put in channels.session) channel-id))
:: initialize sse heartbeat
::
=/ heartbeat-time=@da (add now ~s20)
=/ heartbeat (set-heartbeat-move channel-id heartbeat-time)
:: record the mode & duct for future output,
:: and record heartbeat-time for possible future cancel
::
=. session.channel-state.state
%+ ~(jab by session.channel-state.state) channel-id
|= =channel
%_ channel
mode mode
state [%| duct]
heartbeat (some [heartbeat-time duct])
==
::
[[heartbeat :(weld http-moves cancel-moves moves)] state]
:: +acknowledge-events: removes events before :last-event-id on :channel-id
::
++ acknowledge-events
|= [channel-id=@t last-event-id=@u]
^- server-state
%_ state
session.channel-state
%+ ~(jab by session.channel-state.state) channel-id
|= =channel
^+ channel
=^ acked events.channel
(prune-events events.channel last-event-id)
=. unacked.channel
(subtract-acked-events acked unacked.channel)
channel(last-ack now)
==
:: +on-put-request: handles a PUT request
::
:: PUT requests send commands from the client to the server. We receive
:: a set of commands in JSON format in the body of the message.
::
++ on-put-request
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
|= [channel-id=@t =identity =request:http]
^- [(list move) server-state]
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
:: if the channel already exists, and is not of this identity, 403
::
:: the creation case happens in the +update-timeout-timer-for below
::
?: ?~ c=(~(get by session.channel-state.state) channel-id) |
!=(identity identity.u.c)
%^ return-static-data-on-duct 403 'text/html'
(error-page 403 | url.request ~)
:: error when there's no body
::
?~ body.request
%^ return-static-data-on-duct 400 'text/html'
(error-page 400 %.y url.request "no put body")
::
=/ mode=?(%json %jam)
(find-channel-mode %'PUT' header-list.request)
:: if we cannot parse requests from the body, give an error
::
=/ maybe-requests=(each (list channel-request) @t)
(parse-channel-request mode u.body.request)
?: ?=(%| -.maybe-requests)
%^ return-static-data-on-duct 400 'text/html'
(error-page 400 & url.request (trip p.maybe-requests))
:: while weird, the request list could be empty
::
?: =(~ p.maybe-requests)
%^ return-static-data-on-duct 400 'text/html'
(error-page 400 %.y url.request "empty list of actions")
:: check for the existence of the channel-id
::
:: if we have no session, create a new one set to expire in
2018-11-22 00:37:26 +03:00
:: :channel-timeout from now. if we have one which has a timer, update
:: that timer.
::
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
=. ..on-put-request (update-timeout-timer-for mode identity channel-id)
:: for each request, execute the action passed in
::
=+ requests=p.maybe-requests
:: gall-moves: put moves here first so we can flop for ordering
:: errors: if we accumulate any, discard the gall-moves and revert
::
=| gall-moves=(list move)
=| errors=(map @ud @t)
=/ og-state state
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
=/ from=ship
?- -.identity
%ours our
%fake who.identity
==
|-
::
?~ requests
?: =(~ errors)
:: everything succeeded, mark the request as completed
::
=^ http-moves state
%- handle-response
:* %start
[status-code=204 headers=~]
data=~
complete=%.y
==
::
[:(weld (flop gall-moves) http-moves moves) state]
:: some things went wrong. revert all operations & give 400
::
%- (trace 1 |.("{<channel-id>} reverting due to errors"))
=. state og-state
=^ http-moves state
%^ return-static-data-on-duct 400 'text/html'
%- as-octs:mimes:html
%+ rap 3
%+ turn (sort ~(tap by errors) dor)
|= [id=@ud er=@t]
(rap 3 (crip (a-co:co id)) ': ' er '<br/>' ~)
[(weld http-moves moves) state]
::
?- -.i.requests
%ack
:: client acknowledges that they have received up to event-id
::
%_ $
state (acknowledge-events channel-id event-id.i.requests)
requests t.requests
==
::
?(%poke %poke-json)
=, i.requests
::
?. |(=(from our) =(ship our))
=+ [request-id 'non-local operation']
$(errors (~(put by errors) -), requests t.requests)
::
=. gall-moves
:_ gall-moves
^- move
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
%+ deal-as
/channel/poke/[channel-id]/(scot %ud request-id)
:^ from ship app
^- task:agent:gall
:+ %poke-as mark
?- -.i.requests
%poke [%noun !>(noun)]
%poke-json [%json !>(json)]
2019-11-05 10:42:59 +03:00
==
::
$(requests t.requests)
::
2019-11-14 06:38:13 +03:00
%subscribe
=, i.requests
::
?. |(=(from our) =(ship our))
=+ [request-id 'non-local operation']
$(errors (~(put by errors) -), requests t.requests)
::
::TODO could error if the subscription is a duplicate
=. gall-moves
:_ gall-moves
^- move
2023-02-09 20:55:08 +03:00
%- (trace 1 |.("subscribing to {<app>} on {<path>}"))
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
%+ deal-as
(subscription-wire channel-id request-id ship app)
[from ship app %watch path]
::
=. session.channel-state.state
%+ ~(jab by session.channel-state.state) channel-id
|= =channel
=- channel(subscriptions -)
%+ ~(put by subscriptions.channel)
request-id
[ship app path duct]
::
$(requests t.requests)
::
2019-11-14 06:38:13 +03:00
%unsubscribe
=, i.requests
::
?. |(=(from our) =(ship our))
=+ [request-id 'non-local operation']
$(errors (~(put by errors) -), requests t.requests)
::
=/ usession (~(get by session.channel-state.state) channel-id)
?~ usession
$(requests t.requests)
=/ subscriptions subscriptions:u.usession
::
?~ maybe-subscription=(~(get by subscriptions) subscription-id)
:: the client sent us a weird request referring to a subscription
:: which isn't active.
::
2023-02-09 01:03:02 +03:00
%. $(requests t.requests)
2023-02-09 22:51:34 +03:00
=* msg=tape "{(trip channel-id)} {<subscription-id>}"
2023-02-09 23:06:06 +03:00
(trace 0 |.("missing subscription in unsubscribe {msg}"))
::
=. gall-moves
:_ gall-moves
^- move
=, u.maybe-subscription
2023-02-09 22:22:50 +03:00
%- (trace 1 |.("leaving subscription to {<app>}"))
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
%+ deal-as
(subscription-wire channel-id subscription-id ship app)
[from ship app %leave ~]
::
=. session.channel-state.state
%+ ~(jab by session.channel-state.state) channel-id
|= =channel
%_ channel
subscriptions (~(del by subscriptions.channel) subscription-id)
unacked (~(del by unacked.channel) subscription-id)
==
::
$(requests t.requests)
::
%delete
2023-02-09 21:58:24 +03:00
%- (trace 1 |.("{<channel-id>} discarding due to %delete PUT"))
=^ moves state
(discard-channel channel-id |)
=. gall-moves
(weld gall-moves moves)
$(requests t.requests)
::
==
:: +on-gall-response: sanity-check a gall response, send as event
::
++ on-gall-response
|= [channel-id=@t request-id=@ud extra=wire =sign:agent:gall]
^- [(list move) server-state]
:: if the channel doesn't exist, we should clean up subscriptions
::
:: this is a band-aid solution. you really want eyre to have cleaned
:: these up on-channel-delete in the first place.
:: until the source of that bug is discovered though, we keep this
:: in place to ensure a slightly tidier home.
::
?. ?& !(~(has by session.channel-state.state) channel-id)
?=(?(%fact %watch-ack) -.sign)
?=([@ @ ~] extra)
==
(emit-event channel-id request-id sign)
=/ =ship (slav %p i.extra)
=* app=term i.t.extra
2023-02-09 22:22:50 +03:00
=* msg=tape "{(trip channel-id)} {(trip app)}"
2023-02-09 20:55:08 +03:00
%- (trace 0 |.("removing watch for non-existent channel {msg}"))
:_ state
:_ ~
^- move
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
%+ deal-as
(subscription-wire channel-id request-id ship app)
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
=+ id=identity:(~(got by session.channel-state.state) channel-id)
[id ship app %leave ~]
:: +emit-event: records an event occurred, possibly sending to client
::
:: When an event occurs, we need to record it, even if we immediately
:: send it to a connected browser so in case of disconnection, we can
:: resend it.
::
:: This function is responsible for taking the event sign and converting
:: it into a text/event-stream. The :sign then may get sent, and is
:: stored for later resending until acknowledged by the client.
::
++ emit-event
|= [channel-id=@t request-id=@ud =sign:agent:gall]
^- [(list move) server-state]
::
=/ channel=(unit channel)
(~(get by session.channel-state.state) channel-id)
?~ channel
:_ state :_ ~
[duct %pass /flog %d %flog %crud %eyre-no-channel >id=channel-id< ~]
:: it's possible that this is a sign emitted directly alongside a fact
:: that triggered a clog & closed the subscription. in that case, just
:: drop the sign.
:: poke-acks are not paired with subscriptions, so we can process them
:: regardless.
::
?: ?& !?=(%poke-ack -.sign)
!(~(has by subscriptions.u.channel) request-id)
==
[~ state]
:: attempt to convert the sign to json.
:: if conversion succeeds, we *can* send it. if the client is actually
:: connected, we *will* send it immediately.
::
=/ maybe-channel-event=(unit channel-event)
(sign-to-channel-event sign u.channel request-id)
?~ maybe-channel-event [~ state]
=/ =channel-event u.maybe-channel-event
=/ said=(unit (quip move tape))
(channel-event-to-tape u.channel request-id channel-event)
=? moves ?=(^ said)
(weld moves -.u.said)
=* sending &(?=([%| *] state.u.channel) ?=(^ said))
::
=/ next-id next-id.u.channel
:: if we can send it, store the event as unacked
::
=? events.u.channel ?=(^ said)
%- ~(put to events.u.channel)
[next-id request-id channel-event]
:: if it makes sense to do so, send the event to the client
::
=? moves sending
^- (list move)
:_ moves
::NOTE assertions in this block because =* is flimsy
?> ?=([%| *] state.u.channel)
:+ p.state.u.channel %give
2020-12-08 03:47:06 +03:00
^- gift
:* %response %continue
::
^= data
%- wall-to-octs
(event-tape-to-wall next-id +:(need said))
::
complete=%.n
==
=? next-id ?=(^ said) +(next-id)
:: update channel's unacked counts, find out if clogged
::
=^ clogged unacked.u.channel
:: only apply clog logic to facts.
:: and of course don't count events we can't send as unacked.
::
?: ?| !?=(%fact -.sign)
?=(~ said)
==
[| unacked.u.channel]
=/ num=@ud
(~(gut by unacked.u.channel) request-id 0)
:_ (~(put by unacked.u.channel) request-id +(num))
?& (gte num clog-threshold)
(lth (add last-ack.u.channel clog-timeout) now)
==
:: if we're clogged, or we ran into an event we can't serialize,
:: kill this gall subscription.
::
=* msg=tape "on {(trip channel-id)} for {(scow %ud request-id)}"
2023-02-08 21:47:43 +03:00
=/ kicking=?
?: clogged
2023-02-09 23:06:06 +03:00
((trace 0 |.("clogged {msg}")) &)
?. ?=(~ said) |
2023-02-16 18:02:34 +03:00
((trace 0 |.("can't serialize event, kicking {msg}")) &)
=? moves kicking
:_ moves
::NOTE this shouldn't crash because we
:: - never fail to serialize subscriptionless signs (%poke-ack),
:: - only clog on %facts, which have a subscription associated,
:: - and already checked whether we still have that subscription.
=+ (~(got by subscriptions.u.channel) request-id)
2023-02-09 22:22:50 +03:00
%- (trace 1 |.("leaving subscription to {<app>}"))
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
%+ deal-as
(subscription-wire channel-id request-id ship app)
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
[identity.u.channel ship app %leave ~]
:: update channel state to reflect the %kick
::
=? u.channel kicking
%_ u.channel
subscriptions (~(del by subscriptions.u.channel) request-id)
unacked (~(del by unacked.u.channel) request-id)
events %- ~(put to events.u.channel)
:+ next-id
request-id
(need (sign-to-channel-event [%kick ~] u.channel request-id))
==
:: if a client is connected, send the kick event to them
::
=? moves &(kicking ?=([%| *] state.u.channel))
:_ moves
:+ p.state.u.channel %give
2020-12-08 03:47:06 +03:00
^- gift
:* %response %continue
::
^= data
%- wall-to-octs
%+ event-tape-to-wall next-id
+:(need (channel-event-to-tape u.channel request-id %kick ~))
::
complete=%.n
==
=? next-id kicking +(next-id)
::
:- (flop moves)
%_ state
2018-11-22 00:37:26 +03:00
session.channel-state
%+ ~(put by session.channel-state.state) channel-id
u.channel(next-id next-id)
==
:: +sign-to-channel-event: strip the vase from a sign:agent:gall
::
++ sign-to-channel-event
|= [=sign:agent:gall =channel request-id=@ud]
^- (unit channel-event)
?. ?=(%fact -.sign) `sign
?~ desk=(app-to-desk channel request-id) ~
:- ~
[%fact u.desk [p q.q]:cage.sign]
:: +app-to-desk
::
++ app-to-desk
|= [=channel request-id=@ud]
^- (unit desk)
=/ sub (~(get by subscriptions.channel) request-id)
?~ sub
((trace 0 |.("no subscription for request-id {(scow %ud request-id)}")) ~)
=/ des=(unit (unit cage))
(rof ~ %gd [our app.u.sub da+now] /$)
?. ?=([~ ~ *] des)
2023-02-09 22:51:34 +03:00
((trace 0 |.("no desk for app {<app.u.sub>}")) ~)
`!<(=desk q.u.u.des)
:: +channel-event-to-tape: render channel-event from request-id in specified mode
::
++ channel-event-to-tape
|= [=channel request-id=@ud =channel-event]
^- (unit (quip move tape))
?- mode.channel
%json %+ bind (channel-event-to-json channel request-id channel-event)
|=((quip move json) [+<- (en-json:html +<+)])
%jam =- `[~ (scow %uw (jam -))]
[request-id channel-event]
==
:: +channel-event-to-json: render channel event as json channel event
::
++ channel-event-to-json
~% %eyre-channel-event-to-json ..part ~
|= [=channel request-id=@ud event=channel-event]
^- (unit (quip move json))
:: for facts, we try to convert the result to json
::
=/ [from=(unit [=desk =mark]) jsyn=(unit sign:agent:gall)]
?. ?=(%fact -.event) [~ `event]
?: ?=(%json mark.event)
?~ jsin=((soft json) noun.event)
%. [~ ~]
(slog leaf+"eyre: dropping fake json for {(scow %ud request-id)}" ~)
[~ `[%fact %json !>(u.jsin)]]
:: find and use tube from fact mark to json
::
::
=* have=mark mark.event
=/ convert=(unit vase)
2021-05-20 20:11:45 +03:00
=/ cag=(unit (unit cage))
(rof ~ %cf [our desk.event da+now] /[have]/json)
2021-05-20 20:11:45 +03:00
?. ?=([~ ~ *] cag) ~
`q.u.u.cag
2021-05-20 20:11:45 +03:00
?~ convert
2023-02-09 22:56:50 +03:00
((trace 0 |.("no convert from {(trip have)} to json")) [~ ~])
~| "conversion failed from {(trip have)} to json"
[`[desk.event have] `[%fact %json (slym u.convert noun.event)]]
?~ jsyn ~
%- some
:- ?~ from ~
:_ ~
:^ duct %pass /conversion-cache/[mark.u.from]
[%c %warp our desk.u.from `[%sing %f da+now /[mark.u.from]/json]]
=* sign u.jsyn
=, enjs:format
%- pairs
^- (list [@t json])
:- ['id' (numb request-id)]
?- -.sign
%poke-ack
:~ ['response' [%s 'poke']]
::
?~ p.sign
['ok' [%s 'ok']]
['err' (wall (render-tang-to-wall 100 u.p.sign))]
==
::
%fact
:+ ['response' [%s 'diff']]
:- 'json'
~| [%unexpected-fact-mark p.cage.sign]
?> =(%json p.cage.sign)
!<(json q.cage.sign)
::
?~ from ~
2021-12-02 17:06:42 +03:00
['mark' [%s mark.u.from]]~
::
%kick
['response' [%s 'quit']]~
::
%watch-ack
:~ ['response' [%s 'subscribe']]
::
?~ p.sign
['ok' [%s 'ok']]
['err' (wall (render-tang-to-wall 100 u.p.sign))]
==
==
::
++ event-tape-to-wall
~% %eyre-tape-to-wall ..part ~
|= [event-id=@ud =tape]
^- wall
:~ (weld "id: " (format-ud-as-integer event-id))
(weld "data: " tape)
""
==
::
++ on-channel-heartbeat
|= channel-id=@t
^- [(list move) server-state]
::
=/ res
%- handle-response
:* %continue
data=(some (as-octs:mimes:html ':\0a'))
complete=%.n
==
=/ http-moves -.res
=/ new-state +.res
=/ heartbeat-time=@da (add now ~s20)
:_ %_ new-state
session.channel-state
%+ ~(jab by session.channel-state.state) channel-id
|= =channel
channel(heartbeat (some [heartbeat-time duct]))
==
(snoc http-moves (set-heartbeat-move channel-id heartbeat-time))
:: +discard-channel: remove a channel from state
::
:: cleans up state, timers, and gall subscriptions of the channel
::
++ discard-channel
|= [channel-id=@t expired=?]
^- [(list move) server-state]
::
=/ usession=(unit channel)
(~(get by session.channel-state.state) channel-id)
?~ usession
[~ state]
=/ session=channel u.usession
::
:_ %_ state
session.channel-state
(~(del by session.channel-state.state) channel-id)
::
duct-to-key.channel-state
?. ?=(%| -.state.session) duct-to-key.channel-state.state
(~(del by duct-to-key.channel-state.state) p.state.session)
==
=/ heartbeat-cancel=(list move)
?~ heartbeat.session ~
:~ %^ cancel-heartbeat-move
channel-id
date.u.heartbeat.session
duct.u.heartbeat.session
==
=/ expire-cancel=(list move)
?: expired ~
?. ?=(%& -.state.session) ~
=, p.state.session
[(cancel-timeout-move channel-id date duct)]~
%+ weld heartbeat-cancel
%+ weld expire-cancel
:: produce a list of moves which cancels every gall subscription
::
%+ turn ~(tap by subscriptions.session)
|= [request-id=@ud ship=@p app=term =path duc=^duct]
^- move
2023-02-09 22:22:50 +03:00
%- (trace 1 |.("{<channel-id>} leaving subscription to {<app>}"))
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
%+ deal-as
(subscription-wire channel-id request-id ship app)
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
[identity.session ship app %leave ~]
--
:: +handle-gall-error: a call to +poke-http-response resulted in a %coup
::
++ handle-gall-error
|= =tang
^- [(list move) server-state]
::
?~ connection-state=(~(get by connections.state) duct)
%. `state
(trace 0 |.("{<duct>} error on invalid outstanding connection"))
=* connection u.connection-state
2019-11-13 12:38:18 +03:00
=/ moves-1=(list move)
?. ?=(%app -.action.connection)
~
:_ ~
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
=, connection
%- (trace 1 |.("leaving subscription to {<app.action>}"))
(deal-as /watch-response/[eyre-id] identity our app.action %leave ~)
::
=^ moves-2 state
%^ return-static-data-on-duct 500 'text/html'
::
%- internal-server-error :*
authenticated.inbound-request.connection
url.request.inbound-request.connection
tang
==
2019-11-13 12:38:18 +03:00
[(weld moves-1 moves-2) state]
:: +handle-response: check a response for correctness and send to earth
2018-09-24 21:48:19 +03:00
::
:: All outbound responses including %http-server generated responses need to go
:: through this interface because we want to have one centralized place
:: where we perform logging and state cleanup for connections that we're
:: done with.
::
2018-09-24 21:48:19 +03:00
++ handle-response
|= =http-event:http
2018-09-24 21:48:19 +03:00
^- [(list move) server-state]
:: verify that this is a valid response on the duct
::
?~ connection-state=(~(get by connections.state) duct)
2023-02-09 22:56:50 +03:00
((trace 0 |.("{<duct>} invalid outstanding connection")) `state)
2018-09-24 21:48:19 +03:00
::
|^ ^- [(list move) server-state]
::
?- -.http-event
2018-09-24 21:48:19 +03:00
::
%start
?^ response-header.u.connection-state
2023-02-09 23:06:06 +03:00
((trace 0 |.("{<duct>} error multiple start")) error-connection)
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
:: extend the request's session's + cookie's life
2018-09-24 21:48:19 +03:00
::
=^ response-header sessions.authentication-state.state
=, authentication
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
=* session-id session-id.u.connection-state
=* sessions sessions.authentication-state.state
=* inbound inbound-request.u.connection-state
::
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
?. (~(has by sessions) session-id)
:: if the session has expired since the request was opened,
:: tough luck, we don't create/revive sessions here
::
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
[response-header.http-event sessions]
:_ %+ ~(jab by sessions) session-id
|= =session
session(expiry-time (add now session-timeout))
=- response-header.http-event(headers -)
%^ set-header:http 'set-cookie'
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
(session-cookie-string session-id &)
headers.response-header.http-event
2018-09-24 21:48:19 +03:00
::
=* connection u.connection-state
::
:: if the request was a simple cors request from an approved origin
:: append the necessary cors headers to the response
::
=/ origin=(unit origin)
%+ get-header:http 'origin'
header-list.request.inbound-request.connection
=? headers.response-header
?& ?=(^ origin)
(~(has in approved.cors-registry.state) u.origin)
==
%^ set-header:http 'Access-Control-Allow-Origin' u.origin
%^ set-header:http 'Access-Control-Allow-Credentials' 'true'
headers.response-header
::
=. response-header.http-event response-header
2018-09-24 21:48:19 +03:00
=. connections.state
?: complete.http-event
:: XX optimize by not requiring +put:by in +request
::
(~(del by connections.state) duct)
::
2023-02-09 20:55:08 +03:00
%- (trace 2 |.("{<duct>} start"))
%+ ~(put by connections.state) duct
%= connection
response-header `response-header
bytes-sent ?~(data.http-event 0 p.u.data.http-event)
2018-09-24 21:48:19 +03:00
==
::
pass-response
::
%continue
?~ response-header.u.connection-state
2023-02-09 01:03:02 +03:00
%. error-connection
2023-02-09 23:06:06 +03:00
(trace 0 |.("{<duct>} error continue without start"))
2018-09-24 21:48:19 +03:00
::
=. connections.state
?: complete.http-event
%- (trace 2 |.("{<duct>} completed"))
(~(del by connections.state) duct)
::
%- (trace 2 |.("{<duct>} continuing"))
?~ data.http-event
connections.state
::
%+ ~(put by connections.state) duct
=* size p.u.data.http-event
=* conn u.connection-state
conn(bytes-sent (add size bytes-sent.conn))
2018-09-24 21:48:19 +03:00
::
pass-response
::
%cancel
:: todo: log this differently from an ise.
::
2023-02-09 21:06:02 +03:00
((trace 1 |.("cancel http event")) error-connection)
2018-09-24 21:48:19 +03:00
==
::
++ pass-response
^- [(list move) server-state]
[[duct %give %response http-event]~ state]
2018-09-24 21:48:19 +03:00
::
++ error-connection
:: todo: log application error
::
:: remove all outstanding state for this connection
::
=. connections.state
(~(del by connections.state) duct)
:: respond to outside with %error
::
^- [(list move) server-state]
:_ state
:- [duct %give %response %cancel ~]
?. ?=(%app -.action.u.connection-state)
~
:_ ~
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
=, u.connection-state
2023-02-09 22:22:50 +03:00
%- %+ trace 1
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
|.("leaving subscription to {<app.action>}")
(deal-as /watch-response/[eyre-id] identity our app.action %leave ~)
2018-09-24 21:48:19 +03:00
--
2023-03-25 07:18:25 +03:00
:: +set-response: remember (or update) a cache mapping
::
++ set-response
|= [url=@t entry=(unit cache-entry)]
^- [(list move) server-state]
=/ aeon ?^(prev=(~(get by cache.state) url) +(aeon.u.prev) 1)
=. cache.state (~(put by cache.state) url [aeon entry])
:_ state
[outgoing-duct.state %give %grow /cache/(scot %ud aeon)/(scot %t url)]~
2018-09-20 02:29:36 +03:00
:: +add-binding: conditionally add a pairing between binding and action
::
:: Adds =binding =action if there is no conflicting bindings.
::
++ add-binding
|= [=binding =action]
2018-09-20 02:29:36 +03:00
^- [(list move) server-state]
=^ success bindings.state
:: prevent binding in reserved namespaces
::
?: ?| ?=([%'~' *] path.binding) :: eyre
?=([%'~_~' *] path.binding) :: runtime
==
[| bindings.state]
[& (insert-binding [binding duct action] bindings.state)]
:_ state
[duct %give %bound & binding]~
2018-09-20 02:29:36 +03:00
:: +remove-binding: removes a binding if it exists and is owned by this duct
::
++ remove-binding
|= =binding
::
^- server-state
%_ state
bindings
%+ skip bindings.state
|= [item-binding=^binding item-duct=^duct =action]
2018-09-20 02:29:36 +03:00
^- ?
&(=(item-binding binding) =(item-duct duct))
==
:: +get-action-for-binding: finds an action for an incoming web request
::
++ get-action-for-binding
|= [raw-host=(unit @t) url=@t]
^- [=action suburl=@t]
2018-09-20 02:29:36 +03:00
:: process :raw-host
::
:: If we are missing a 'Host:' header, if that header is a raw IP
:: address, or if the 'Host:' header refers to [our].urbit.org, we want
:: to return ~ which means we're unidentified and will match against any
:: wildcard matching.
2018-09-20 02:29:36 +03:00
::
:: Otherwise, return the site given.
::
=/ host=(unit @t)
?~ raw-host
~
:: Parse the raw-host so that we can ignore ports, usernames, etc.
2018-09-20 02:29:36 +03:00
::
2018-10-04 00:05:36 +03:00
=+ parsed=(rush u.raw-host simplified-url-parser)
?~ parsed
2018-09-20 02:29:36 +03:00
~
:: if the url is a raw IP, assume default site.
::
2018-10-04 00:05:36 +03:00
?: ?=([%ip *] -.u.parsed)
~
:: if the url is "localhost", assume default site.
::
?: =([%site 'localhost'] -.u.parsed)
~
:: render our as a tape, and cut off the sig in front.
::
=/ with-sig=tape (scow %p our)
?> ?=(^ with-sig)
?: =(u.raw-host (crip t.with-sig))
:: [our].urbit.org is the default site
::
~
2018-09-20 02:29:36 +03:00
::
raw-host
:: url is the raw thing passed over the 'Request-Line'.
::
2018-09-27 02:18:40 +03:00
:: todo: this is really input validation, and we should return a 500 to
:: the client.
::
=/ request-line (parse-request-line url)
=/ parsed-url=(list @t) site.request-line
=? parsed-url ?=([%'~' %channel-jam *] parsed-url)
parsed-url(i.t %channel)
2018-09-20 02:29:36 +03:00
::
=/ bindings bindings.state
|-
::
?~ bindings
[[%four-oh-four ~] url]
::
?. (host-matches site.binding.i.bindings raw-host)
$(bindings t.bindings)
?~ suffix=(find-suffix path.binding.i.bindings parsed-url)
$(bindings t.bindings)
::
:- action.i.bindings
%^ cat 3
%+ roll
^- (list @t)
(join '/' (flop ['' u.suffix]))
(cury cat 3)
?~ ext.request-line ''
(cat 3 '.' u.ext.request-line)
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
:: +give-session-tokens: send valid local session tokens to unix
::
++ give-session-tokens
^- move
:- outgoing-duct.state
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
:+ %give %sessions
%- sy
%+ murn ~(tap by sessions.authentication-state.state)
|= [sid=@uv session]
?. ?=(%ours -.identity) ~
(some (scot %uv sid))
:: +new-session-key
::
++ new-session-key
|- ^- @uv
=/ candidate=@uv (~(raw og (shas %session-key eny)) 128)
?. (~(has by sessions.authentication-state.state) candidate)
candidate
$(eny (shas %try-again candidate))
::
++ deal-as
|= [=wire identity=$@(@p identity) =ship =dude:gall =task:agent:gall]
^- move
=/ from=@p
?@ identity identity
?-(-.identity %ours our, %fake who.identity)
[duct %pass wire %g %deal [from ship] dude task]
2023-02-08 22:43:13 +03:00
::
++ trace
|= [pri=@ print=(trap tape)]
2023-02-08 23:19:14 +03:00
?: (lth verb.state pri) same
2023-02-08 22:43:13 +03:00
(slog leaf+"eyre: {(print)}" ~)
2018-09-20 02:29:36 +03:00
--
::
++ forwarded-params
|= =header-list:http
^- (unit (list (map @t @t)))
%+ biff
(get-header:http 'forwarded' header-list)
unpack-header:http
::
++ forwarded-for
|= forwards=(list (map @t @t))
^- (unit address)
?. ?=(^ forwards) ~
=* forward i.forwards
?~ for=(~(get by forward) 'for') ~
::NOTE per rfc7239, non-ip values are also valid. they're not useful
:: for the general case, so we ignore them here. if needed,
:: request handlers are free to inspect the headers themselves.
::
%+ rush u.for
;~ sfix
2020-11-15 08:08:52 +03:00
;~(pose (stag %ipv4 ip4) (stag %ipv6 (ifix [sel ser] ip6)))
;~(pose ;~(pfix col dim:ag) (easy ~))
==
::
++ forwarded-secure
|= forwards=(list (map @t @t))
^- (unit ?)
?. ?=(^ forwards) ~
=* forward i.forwards
?~ proto=(~(get by forward) 'proto') ~
?+ u.proto ~
%http `|
%https `&
==
::
++ parse-request-line
|= url=@t
^- [[ext=(unit @ta) site=(list @t)] args=(list [key=@t value=@t])]
(fall (rush url ;~(plug apat:de-purl:html yque:de-purl:html)) [[~ ~] ~])
:: +insert-binding: add a new binding, replacing any existing at its path
::
++ insert-binding
|= $: new=[=binding =duct =action]
bindings=(list [=binding =duct =action])
==
^+ bindings
?~ bindings [new]~
=* bid binding.i.bindings
:: replace already bound paths
::
?: =([site path]:bid [site path]:binding.new)
~> %slog.[0 leaf+"eyre: replacing existing binding at {<`path`path.bid>}"]
[new t.bindings]
:: if new comes before bid, prepend it.
:: otherwise, continue our search.
::
=; new-before-bid=?
?: new-before-bid [new bindings]
[i.bindings $(bindings t.bindings)]
?: =(site.binding.new site.bid)
(aor path.bid path.binding.new)
(aor (fall site.bid '') (fall site.binding.new ''))
::
++ channel-wire
|= [channel-id=@t request-id=@ud]
^- wire
/channel/subscription/[channel-id]/(scot %ud request-id)
::
++ subscription-wire
|= [channel-id=@t request-id=@ud =ship app=term]
^- wire
(weld (channel-wire channel-id request-id) /(scot %p ship)/[app])
2018-09-20 02:29:36 +03:00
--
:: end the =~
::
. ==
:: begin with a default +axle as a blank slate
::
=| ax=axle
:: a vane is activated with current date, entropy, and a namespace function
::
2020-12-06 11:38:37 +03:00
|= [now=@da eny=@uvJ rof=roof]
2018-09-20 02:29:36 +03:00
:: allow jets to be registered within this core
::
~% %http-server ..part ~
2018-09-20 02:29:36 +03:00
|%
++ call
2021-03-28 10:47:37 +03:00
~/ %eyre-call
2020-12-08 03:47:06 +03:00
|= [=duct dud=(unit goof) wrapped-task=(hobo task)]
^- [(list move) _http-server-gate]
2018-09-20 02:29:36 +03:00
::
2020-12-08 03:47:06 +03:00
=/ task=task ((harden task) wrapped-task)
::
2020-12-08 05:01:48 +03:00
:: XX handle error notifications
::
2020-12-08 05:01:48 +03:00
?^ dud
2019-03-13 23:29:22 +03:00
=/ moves=(list move)
2020-12-08 05:01:48 +03:00
[[duct %slip %d %flog %crud [-.task tang.u.dud]] ~]
2019-03-13 23:29:22 +03:00
[moves http-server-gate]
:: %init: tells us what our ship name is
2018-09-20 02:29:36 +03:00
::
?: ?=(%init -.task)
2018-09-27 02:18:40 +03:00
:: initial value for the login handler
::
=. bindings.server-state.ax
=- (roll - insert-binding)
^- (list [binding ^duct action])
:~ [[~ /~/login] duct [%authentication ~]]
[[~ /~/logout] duct [%logout ~]]
[[~ /~/channel] duct [%channel ~]]
[[~ /~/scry] duct [%scry ~]]
2022-08-01 01:50:36 +03:00
[[~ /~/name] duct [%name ~]]
[[~ /~/host] duct [%host ~]]
2018-09-27 02:18:40 +03:00
==
[~ http-server-gate]
:: %trim: in response to memory pressure
::
:: Cancel all inactive channels
:: XX cancel active too if =(0 trim-priority) ?
::
?: ?=(%trim -.task)
=* event-args [[eny duct now rof] server-state.ax]
=* by-channel by-channel:(per-server-event event-args)
=* channel-state channel-state.server-state.ax
::
=/ inactive=(list @t)
=/ full=(set @t) ~(key by session.channel-state)
=/ live=(set @t)
(~(gas in *(set @t)) ~(val by duct-to-key.channel-state))
~(tap in (~(dif in full) live))
::
?: =(~ inactive)
[~ http-server-gate]
::
=/ len=tape (scow %ud (lent inactive))
~> %slog.[0 leaf+"eyre: trim: closing {len} inactive channels"]
::
=| moves=(list (list move))
|- ^- [(list move) _http-server-gate]
=* channel-id i.inactive
?~ inactive
[(zing (flop moves)) http-server-gate]
:: discard channel state, and cancel any active gall subscriptions
::
=^ mov server-state.ax (discard-channel:by-channel channel-id |)
$(moves [mov moves], inactive t.inactive)
::
:: %vega: notifies us of a completed kernel upgrade
::
?: ?=(%vega -.task)
[~ http-server-gate]
:: %born: new unix process
::
?: ?=(%born -.task)
2018-10-24 21:25:55 +03:00
:: close previously open connections
::
2018-10-24 21:25:55 +03:00
:: When we have a new unix process, every outstanding open connection is
:: dead. For every duct, send an implicit close connection.
::
=^ closed-connections=(list move) server-state.ax
=/ connections=(list [=^duct *])
~(tap by connections.server-state.ax)
::
=| closed-connections=(list move)
|-
?~ connections
[closed-connections server-state.ax]
::
=/ event-args
2020-12-06 13:55:19 +03:00
[[eny duct.i.connections now rof] server-state.ax]
2018-10-24 21:25:55 +03:00
=/ cancel-request cancel-request:(per-server-event event-args)
=^ moves server-state.ax cancel-request
::
$(closed-connections (weld moves closed-connections), connections t.connections)
:: save duct for future %give to unix
::
=. outgoing-duct.server-state.ax duct
2023-03-25 07:18:25 +03:00
:: send all cache mappings to runtime
::
=/ cache-moves=(list move)
%+ turn ~(tap by cache.server-state.ax)
|= [url=@t cache-val=[aeon=@ud val=(unit cache-entry)]]
[duct %give %grow /cache/(scot %u aeon.cache-val)/(scot %t url)]
2018-10-24 21:25:55 +03:00
::
:_ http-server-gate
:* :: hand back default configuration for now
::
[duct %give %set-config http-config.server-state.ax]
:: provide a list of valid auth tokens
::
=< give-session-tokens
(per-server-event [eny duct now rof] server-state.ax)
2018-10-24 21:25:55 +03:00
::
2023-03-25 07:18:25 +03:00
(zing ~[closed-connections cache-moves])
2018-10-24 21:25:55 +03:00
==
::
?: ?=(%code-changed -.task)
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
~> %slog.[0 leaf+"eyre: code-changed: throwing away local sessions"]
=* event-args [[eny duct now rof] server-state.ax]
=* auth authentication:(per-server-event event-args)
:: find all the %ours sessions, we must close them
::
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
=/ siz=(list @uv)
%+ murn ~(tap by sessions.authentication-state.server-state.ax)
|= [sid=@uv session]
?:(?=(%ours -.identity) (some sid) ~)
=| moves=(list (list move))
|- ^- [(list move) _http-server-gate]
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
?~ siz
[(zing (flop moves)) http-server-gate]
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
:: discard the session, clean up its channels
::
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
=^ mov server-state.ax (close-session:auth i.siz |)
$(moves [mov moves], siz t.siz)
::
:: all other commands operate on a per-server-event
::
2020-12-06 13:55:19 +03:00
=/ event-args [[eny duct now rof] server-state.ax]
=/ server (per-server-event event-args)
::
?- -.task
:: %live: notifies us of the ports of our live http servers
::
%live
=. ports.server-state.ax +.task
:: enable http redirects if https port live and cert set
::
=. redirect.http-config.server-state.ax
&(?=(^ secure.task) ?=(^ secure.http-config.server-state.ax))
[~ http-server-gate]
:: %rule: updates our http configuration
::
%rule
?- -.http-rule.task
:: %cert: install tls certificate
::
%cert
=* config http-config.server-state.ax
?: =(secure.config cert.http-rule.task)
[~ http-server-gate]
=. secure.config cert.http-rule.task
=. redirect.config
?& ?=(^ secure.ports.server-state.ax)
?=(^ cert.http-rule.task)
==
:_ http-server-gate
=* out-duct outgoing-duct.server-state.ax
?~ out-duct ~
[out-duct %give %set-config config]~
:: %turf: add or remove domain name
::
%turf
=* domains domains.server-state.ax
=/ mod=(set turf)
?: ?=(%put action.http-rule.task)
(~(put in domains) turf.http-rule.task)
(~(del in domains) turf.http-rule.task)
?: =(domains mod)
[~ http-server-gate]
=. domains mod
:_ http-server-gate
=/ cmd
[%acme %poke `cage`[%acme-order !>(mod)]]
2019-11-19 07:36:21 +03:00
[duct %pass /acme/order %g %deal [our our] cmd]~
==
::
2019-11-14 06:38:13 +03:00
%request
=^ moves server-state.ax (request:server +.task)
[moves http-server-gate]
::
%request-local
=^ moves server-state.ax (request-local:server +.task)
[moves http-server-gate]
::
%cancel-request
=^ moves server-state.ax cancel-request:server
[moves http-server-gate]
::
%connect
=^ moves server-state.ax
%+ add-binding:server binding.task
[%app app.task]
[moves http-server-gate]
::
%serve
=^ moves server-state.ax
%+ add-binding:server binding.task
[%gen generator.task]
[moves http-server-gate]
::
%disconnect
=. server-state.ax (remove-binding:server binding.task)
[~ http-server-gate]
::
%approve-origin
=. cors-registry.server-state.ax
=, cors-registry.server-state.ax
:+ (~(del in requests) origin.task)
(~(put in approved) origin.task)
(~(del in rejected) origin.task)
[~ http-server-gate]
::
%reject-origin
=. cors-registry.server-state.ax
=, cors-registry.server-state.ax
:+ (~(del in requests) origin.task)
(~(del in approved) origin.task)
(~(put in rejected) origin.task)
[~ http-server-gate]
2023-02-08 23:20:07 +03:00
::
%spew
=. verb.server-state.ax veb.task
`http-server-gate
2023-03-25 07:18:25 +03:00
::
%set-response
=^ moves server-state.ax (set-response:server +.task)
[moves http-server-gate]
2018-09-20 02:29:36 +03:00
==
::
2018-09-24 21:48:19 +03:00
++ take
2021-03-28 10:47:37 +03:00
~/ %eyre-take
2020-12-06 11:38:37 +03:00
|= [=wire =duct dud=(unit goof) =sign]
^- [(list move) _http-server-gate]
?^ dud
~|(%eyre-take-dud (mean tang.u.dud))
2019-11-05 10:42:59 +03:00
=> %= .
sign
2020-12-08 03:22:26 +03:00
?: ?=(%gall -.sign)
2019-11-05 10:42:59 +03:00
?> ?=(%unto +<.sign)
sign
sign
==
2018-09-24 21:48:19 +03:00
:: :wire must at least contain two parts, the type and the build
::
2018-10-11 01:26:51 +03:00
?> ?=([@ *] wire)
2018-09-24 21:48:19 +03:00
::
|^ ^- [(list move) _http-server-gate]
2018-09-24 21:48:19 +03:00
::
?+ i.wire
~|([%bad-take-wire wire] !!)
2018-09-24 21:48:19 +03:00
::
%run-app-request run-app-request
%watch-response watch-response
%sessions sessions
%channel channel
%acme acme-ack
%conversion-cache `http-server-gate
==
2018-09-24 21:48:19 +03:00
::
++ run-app-request
::
2020-12-08 03:22:26 +03:00
?> ?=([%gall %unto *] sign)
::
::
?> ?=([%poke-ack *] p.sign)
?> ?=([@ *] t.wire)
?~ p.p.sign
:: received a positive acknowledgment: take no action
::
[~ http-server-gate]
:: we have an error; propagate it to the client
::
2020-12-06 13:55:19 +03:00
=/ event-args [[eny duct now rof] server-state.ax]
=/ handle-gall-error
handle-gall-error:(per-server-event event-args)
=^ moves server-state.ax
(handle-gall-error u.p.p.sign)
[moves http-server-gate]
::
++ watch-response
::
2020-12-06 13:55:19 +03:00
=/ event-args [[eny duct now rof] server-state.ax]
::
?> ?=([@ *] t.wire)
2020-12-08 03:22:26 +03:00
?: ?=([%gall %unto %watch-ack *] sign)
?~ p.p.sign
:: received a positive acknowledgment: take no action
::
[~ http-server-gate]
:: we have an error; propagate it to the client
::
=/ handle-gall-error
handle-gall-error:(per-server-event event-args)
=^ moves server-state.ax (handle-gall-error u.p.p.sign)
[moves http-server-gate]
::
2020-12-08 03:22:26 +03:00
?: ?=([%gall %unto %kick ~] sign)
=/ handle-response handle-response:(per-server-event event-args)
=^ moves server-state.ax
(handle-response %continue ~ &)
[moves http-server-gate]
2018-09-24 21:48:19 +03:00
::
2020-12-08 03:22:26 +03:00
?> ?=([%gall %unto %fact *] sign)
=/ =mark p.cage.p.sign
=/ =vase q.cage.p.sign
?. ?= ?(%http-response-header %http-response-data %http-response-cancel)
mark
=/ handle-gall-error
handle-gall-error:(per-server-event event-args)
=^ moves server-state.ax
(handle-gall-error leaf+"eyre bad mark {(trip mark)}" ~)
[moves http-server-gate]
::
=/ =http-event:http
?- mark
%http-response-header [%start !<(response-header:http vase) ~ |]
%http-response-data [%continue !<((unit octs) vase) |]
%http-response-cancel [%cancel ~]
==
2018-09-24 21:48:19 +03:00
=/ handle-response handle-response:(per-server-event event-args)
=^ moves server-state.ax
(handle-response http-event)
[moves http-server-gate]
::
++ channel
::
2020-12-06 13:55:19 +03:00
=/ event-args [[eny duct now rof] server-state.ax]
:: channel callback wires are triples.
::
?> ?=([@ @ @t *] wire)
::
?+ i.t.wire
~|([%bad-channel-wire wire] !!)
::
%timeout
2020-12-08 03:22:26 +03:00
?> ?=([%behn %wake *] sign)
?^ error.sign
[[duct %slip %d %flog %crud %wake u.error.sign]~ http-server-gate]
2023-02-16 18:02:34 +03:00
=* id i.t.t.wire
%- %+ trace:(per-server-event event-args) 1
|.("{(trip id)} cancelling channel due to timeout")
=^ moves server-state.ax
2023-02-09 22:11:35 +03:00
(discard-channel:by-channel:(per-server-event event-args) id &)
[moves http-server-gate]
::
%heartbeat
=/ on-channel-heartbeat
on-channel-heartbeat:by-channel:(per-server-event event-args)
=^ moves server-state.ax
(on-channel-heartbeat i.t.t.wire)
[moves http-server-gate]
::
?(%poke %subscription)
2020-12-08 03:22:26 +03:00
?> ?=([%gall %unto *] sign)
~| eyre-sub=wire
?> ?=([@ @ @t @ *] wire)
?< ?=(%raw-fact -.p.sign)
=* channel-id i.t.t.wire
=* request-id i.t.t.t.wire
=* extra-wire t.t.t.t.wire
=/ on-gall-response
on-gall-response:by-channel:(per-server-event event-args)
:: ~& [%gall-response sign]
=^ moves server-state.ax
%- on-gall-response
[channel-id (slav %ud request-id) extra-wire p.sign]
[moves http-server-gate]
==
::
++ sessions
::
2020-12-08 03:22:26 +03:00
?> ?=([%behn %wake *] sign)
::
?^ error.sign
[[duct %slip %d %flog %crud %wake u.error.sign]~ http-server-gate]
::NOTE we are not concerned with expiring channels that are still in
:: use. we require acks for messages, which bump their session's
:: timer. channels have their own expiry timer, too.
:: remove cookies that have expired
::
=* sessions sessions.authentication-state.server-state.ax
=. sessions.authentication-state.server-state.ax
%- ~(gas by *(map @uv session))
%+ skip ~(tap in sessions)
|= [cookie=@uv session]
(lth expiry-time now)
:: if there's any cookies left, set a timer for the next expected expiry
::
^- [(list move) _http-server-gate]
:_ http-server-gate
:- =< give-session-tokens
(per-server-event [eny duct now rof] server-state.ax)
?: =(~ sessions) ~
=; next-expiry=@da
[duct %pass /sessions/expire %b %wait next-expiry]~
%+ roll ~(tap by sessions)
|= [[@uv session] next=@da]
?: =(*@da next) expiry-time
(min next expiry-time)
::
++ acme-ack
2020-12-08 03:22:26 +03:00
?> ?=([%gall %unto *] sign)
::
2019-11-05 10:42:59 +03:00
?> ?=([%poke-ack *] p.sign)
?~ p.p.sign
:: received a positive acknowledgment: take no action
::
[~ http-server-gate]
:: received a negative acknowledgment: XX do something
::
[((slog u.p.p.sign) ~) http-server-gate]
2018-09-24 21:48:19 +03:00
--
::
++ http-server-gate ..$
:: +load: migrate old state to new state (called on vane reload)
::
++ load
2023-02-08 23:10:16 +03:00
=> |%
+$ axle-any
$% [date=%~2020.10.18 server-state=server-state-0]
[date=%~2022.7.26 server-state=server-state-0]
[date=%~2023.2.17 server-state=server-state-1]
[date=%~2023.3.16 server-state=server-state-2]
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
[date=%~2023.4.11 server-state-3]
[date=%~2023.5.3 server-state]
2023-02-08 23:10:16 +03:00
==
::
2023-02-08 23:10:16 +03:00
+$ server-state-0
$: bindings=(list [=binding =duct =action])
=cors-registry
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
connections=(map duct outstanding-connection-3)
authentication-state=authentication-state-3
channel-state=channel-state-2
2023-02-08 23:10:16 +03:00
domains=(set turf)
=http-config
ports=[insecure=@ud secure=(unit @ud)]
outgoing-duct=duct
==
::
2023-03-25 07:18:25 +03:00
+$ server-state-1
$: bindings=(list [=binding =duct =action])
=cors-registry
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
connections=(map duct outstanding-connection-3)
authentication-state=authentication-state-3
channel-state=channel-state-2
domains=(set turf)
=http-config
ports=[insecure=@ud secure=(unit @ud)]
outgoing-duct=duct
verb=@ :: <- new
==
::
+$ server-state-2
$: bindings=(list [=binding =duct =action])
cache=(map url=@t [aeon=@ud val=(unit cache-entry)]) :: <- new
=cors-registry
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
connections=(map duct outstanding-connection-3)
authentication-state=authentication-state-3
channel-state=channel-state-2
2023-03-25 07:18:25 +03:00
domains=(set turf)
=http-config
ports=[insecure=@ud secure=(unit @ud)]
outgoing-duct=duct
verb=@
==
+$ channel-state-2
$: session=(map @t channel-2)
duct-to-key=(map duct @t)
==
+$ channel-2
$: state=(each timer duct)
next-id=@ud
last-ack=@da
events=(qeu [id=@ud request-id=@ud channel-event=channel-event-2])
unacked=(map @ud @ud)
subscriptions=(map @ud [ship=@p app=term =path duc=duct])
heartbeat=(unit timer)
==
+$ channel-event-2
$% $>(%poke-ack sign:agent:gall)
$>(%watch-ack sign:agent:gall)
$>(%kick sign:agent:gall)
[%fact =mark =noun]
==
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
::
+$ server-state-3
$: bindings=(list [=binding =duct =action])
cache=(map url=@t [aeon=@ud val=(unit cache-entry)])
=cors-registry
connections=(map duct outstanding-connection-3)
authentication-state=authentication-state-3
channel-state=channel-state-3
domains=(set turf)
=http-config
ports=[insecure=@ud secure=(unit @ud)]
outgoing-duct=duct
verb=@
==
+$ outstanding-connection-3
$: =action
=inbound-request
response-header=(unit response-header:http)
bytes-sent=@ud
==
+$ authentication-state-3 sessions=(map @uv session-3)
+$ session-3
$: expiry-time=@da
channels=(set @t)
==
+$ channel-state-3
$: session=(map @t channel-3)
duct-to-key=(map duct @t)
==
+$ channel-3
$: mode=?(%json %jam)
state=(each timer duct)
next-id=@ud
last-ack=@da
events=(qeu [id=@ud request-id=@ud =channel-event])
unacked=(map @ud @ud)
subscriptions=(map @ud [ship=@p app=term =path duc=duct])
heartbeat=(unit timer)
==
2023-02-08 23:10:16 +03:00
--
|= old=axle-any
^+ http-server-gate
2022-08-01 01:50:36 +03:00
?- -.old
::
:: adds /~/name
::
2022-08-01 01:50:36 +03:00
%~2020.10.18
2022-08-11 19:32:49 +03:00
%= $
date.old %~2022.7.26
::
bindings.server-state.old
%+ insert-binding
[[~ /~/name] outgoing-duct.server-state.old [%name ~]]
bindings.server-state.old
==
::
:: enables https redirects if certificate configured
:: inits .verb
2022-08-01 01:50:36 +03:00
::
%~2022.7.26
=. redirect.http-config.server-state.old
?& ?=(^ secure.ports.server-state.old)
?=(^ secure.http-config.server-state.old)
==
$(old [%~2023.2.17 server-state.old(|8 [|8 verb=0]:server-state.old)])
::
:: inits .cache
::
2023-02-17 19:15:27 +03:00
%~2023.2.17
$(old [%~2023.3.16 [bindings ~ +]:server-state.old])
::
:: inits channel mode and desks in unacked events
::
2023-03-25 07:18:25 +03:00
%~2023.3.16
::
:: Prior to this desks were not part of events.channel.
:: When serializing we used to rely on the desk stored in
:: subscriptions.channel, but this state is deleted when we clog.
:: This migration adds the desk to events.channel, but we can not
:: scry in +load to populate the desks in the old events,
:: so we just kick all subscriptions on all channels.
%= $
date.old %~2023.4.11
::
server-state.old
%= server-state.old
session.channel-state
%- ~(run by session.channel-state.server-state.old)
|= c=channel-2
=; new-events
:- %json
c(events new-events, unacked ~, subscriptions ~)
=| events=(qeu [id=@ud request-id=@ud =channel-event])
=/ l ~(tap in ~(key by subscriptions.c))
|-
?~ l events
%= $
l t.l
next-id.c +(next-id.c)
events (~(put to events) [next-id.c i.l %kick ~])
==
==
==
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
::
:: guarantees & stores a session for each request, and a @p identity for
:: each session and channel
::
%~2023.4.11
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
%= $
date.old %~2023.5.3
::
connections.old
%- ~(run by connections.old)
|= outstanding-connection-3
^- outstanding-connection
[action inbound-request [*@uv [%ours ~]] response-header bytes-sent]
::
sessions.authentication-state.old
%- ~(run by sessions.authentication-state.old)
|= s=session-3
^- session
[[%ours ~] s]
::
session.channel-state.old
%- ~(run by session.channel-state.old)
|= c=channel-3
^- channel
[-.c [%ours ~] +.c]
::
bindings.old
%+ insert-binding
[[~ /~/host] outgoing-duct.old [%host ~]]
bindings.old
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
==
::
%~2023.5.3
http-server-gate(ax old)
2023-02-08 23:10:16 +03:00
==
:: +stay: produce current state
::
++ stay `axle`ax
:: +scry: request a path in the urbit namespace
::
++ scry
2021-03-28 10:47:37 +03:00
~/ %eyre-scry
2020-12-08 00:52:12 +03:00
^- roon
|= [lyc=gang car=term bem=beam]
2019-03-21 09:01:38 +03:00
^- (unit (unit cage))
2020-12-08 00:52:12 +03:00
=* ren car
2020-11-24 00:06:50 +03:00
=* why=shop &/p.bem
=* syd q.bem
=/ lot=coin $/r.bem
=* tyl s.bem
::
2019-03-21 09:01:38 +03:00
?. ?=(%& -.why)
~
=* who p.why
::
2019-03-21 09:01:38 +03:00
?. ?=(%$ -.lot)
[~ ~]
?. =(our who)
?. =([%da now] p.lot)
[~ ~]
~& [%r %scry-foreign-host who]
~
?: &(?=(%x ren) ?=(%$ syd))
=, server-state.ax
?+ tyl [~ ~]
[%$ %whey ~] =- ``mass+!>(`(list mass)`-)
:~ bindings+&+bindings.server-state.ax
auth+&+authentication-state.server-state.ax
connections+&+connections.server-state.ax
channels+&+channel-state.server-state.ax
axle+&+ax
==
::
[%cors ~] ``noun+!>(cors-registry)
[%cors %requests ~] ``noun+!>(requests.cors-registry)
[%cors %approved ~] ``noun+!>(approved.cors-registry)
[%cors %rejected ~] ``noun+!>(rejected.cors-registry)
::
[%cors ?(%approved %rejected) @ ~]
=* kind i.t.tyl
=* orig i.t.t.tyl
?~ origin=(slaw %t orig) [~ ~]
?- kind
%approved ``noun+!>((~(has in approved.cors-registry) u.origin))
%rejected ``noun+!>((~(has in rejected.cors-registry) u.origin))
==
::
[%authenticated %cookie @ ~]
?~ cookies=(slaw %t i.t.t.tyl) [~ ~]
:^ ~ ~ %noun
!> ^- ?
eyre: guest ids for unauthenticated requests aka "the open eyre" aka "universal basic identity" Urbit already supports presence on the clearnet, but fails to expose any of its interactive affordances to unauthenticated users. Here, we improve this situation by granting "guest identity" @ps to every unauthenticated HTTP request, and extending the channels functionality to them. Sessions no longer represent only the local identity. Instead, each session has either the local identity, or a fake guest identity associated with it. Every request that does not provide a session key/cookie gets assigned a fresh one with a guest identity on the spot. As a result, every single request has an identity associated with it. The identity of a request gets propagated into userspace, if the request ends up there. For normal HTTP requests, this means the src.bowl gets set to that identity for both the watch and poke of the request. For backwards compatibility, the authenticated flag on the request noun gets set at normal: only true if the request came from the local identity. For channel requests, this means the src.bowl gets set to that identity for any pokes and watches it sends, and it can only send those to agents running on the local ship. The scry endpoint remains unchanged in its behavior: only available to the local identity. Notable implementation detail changes in this diff include: - Factored all gall interactions out into +deal-as. - Sessions no longer represent exclusively the local identity. This matters a lot to +give-session-tokens, %code-changed, and logout handling. - Session management got factored out into explicit +start-session and +close-session arms.
2023-05-05 22:59:17 +03:00
%- =< request-is-authenticated:authentication
2020-12-06 13:55:19 +03:00
(per-server-event [eny *duct now rof] server-state.ax)
%*(. *request:http header-list ['cookie' u.cookies]~)
2023-03-25 07:18:25 +03:00
::
[%cache @ @ ~]
?~ aeon=(slaw %ud i.t.tyl) [~ ~]
?~ url=(slaw %t i.t.t.tyl) [~ ~]
?~ entry=(~(get by cache) u.url) [~ ~]
?. =(u.aeon aeon.u.entry) [~ ~]
?~ val=val.u.entry [~ ~]
``noun+!>(u.val)
==
?. ?=(%$ ren)
[~ ~]
?+ syd [~ ~]
%bindings ``noun+!>(bindings.server-state.ax)
%connections ``noun+!>(connections.server-state.ax)
%authentication-state ``noun+!>(authentication-state.server-state.ax)
%channel-state ``noun+!>(channel-state.server-state.ax)
2019-03-21 09:01:38 +03:00
::
%host
%- (lift (lift |=(a=hart:eyre [%hart !>(a)])))
^- (unit (unit hart:eyre))
=. p.lot ?.(=([%da now] p.lot) p.lot [%tas %real])
?+ p.lot
[~ ~]
::
[%tas %fake]
``[& [~ 8.443] %& /localhost]
::
[%tas %real]
=* domains domains.server-state.ax
=* ports ports.server-state.ax
=/ =host:eyre [%& ?^(domains n.domains /localhost)]
=/ port=(unit @ud)
?. ?=(^ secure.ports)
?:(=(80 insecure.ports) ~ `insecure.ports)
?:(=(443 u.secure.ports) ~ secure.ports)
``[?=(^ secure.ports) port host]
==
2019-03-21 09:01:38 +03:00
==
2018-09-20 02:29:36 +03:00
--