Skip to content

Commit

Permalink
Add ServiceNow, so far just with logging in via OAuth
Browse files Browse the repository at this point in the history
  • Loading branch information
achlipala committed Aug 27, 2023
1 parent de6a65c commit b12db6a
Show file tree
Hide file tree
Showing 6 changed files with 149 additions and 0 deletions.
16 changes: 16 additions & 0 deletions examples/serviceNowDemo.ur
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
(* For this demo, it's necessary to create serviceNowSecrets.ur,
* defining [instance], [client_id], and [client_secret]. *)

structure A = ServiceNow.ThreeLegged(struct
open ServiceNowSecrets
val https = False
val onCompletion = return <xml>Done.</xml>
val scopes = ServiceNow.Scope.empty
end)
structure S = ServiceNow.Make(A)

val main =
toko <- A.token;
case toko of
None => return <xml><body><a link={A.authorize}>Log into ServiceNow</a></body></xml>
| Some _ => return <xml><body>You are logged in.</body></xml>
10 changes: 10 additions & 0 deletions examples/serviceNowDemo.urp
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
library ../src/ur
rewrite all ServiceNowDemo/*
database dbname=serviceNowDemo
sql serviceNowDemo.sql
safeGetDefault
allow url https://*
prefix http://localhost:8080/

serviceNowSecrets
serviceNowDemo
1 change: 1 addition & 0 deletions examples/serviceNowDemo.urs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
val main : transaction page
1 change: 1 addition & 0 deletions src/ur/lib.urp
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,4 @@ zenefits
netSuite
chatgpt
smartsheet
serviceNow
93 changes: 93 additions & 0 deletions src/ur/serviceNow.ur
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
open Json

structure Scope = struct
type t = Scopes.t []
val empty = Scopes.empty
val union = Scopes.union
val toString = Scopes.toString {}

val readonly = Scopes.disjoint empty
end

signature AUTH = sig
val token : transaction (option string)
end

functor Make(M : AUTH) = struct
open M
end

functor ThreeLegged(M : sig
val instance : string
val client_id : string
val client_secret : string
val https : bool

val scopes : Scope.t
val onCompletion : transaction page
end) = struct
open M

table secrets : { Secret : int,
Token : string,
Expires : time }
PRIMARY KEY Secret

task periodic 60 = fn () =>
tm <- now;
dml (DELETE FROM secrets
WHERE Expires < {[addSeconds tm (-60)]})

cookie user : int

fun withToken {Token = tok, Expiration = seconds, ...} =
case seconds of
None => error <xml>Missing token expiration in OAuth response</xml>
| Some seconds =>
secret <- rand;
tm <- now;
dml (INSERT INTO secrets(Secret, Token, Expires)
VALUES ({[secret]}, {[tok]}, {[addSeconds tm (seconds * 3 / 4)]}));
setCookie user {Value = secret,
Expires = None,
Secure = https}

open Oauth.Make(struct
open M

val authorize_url = bless ("https://" ^ instance ^ ".service-now.com/oauth_auth.do")
val access_token_url = bless ("https://" ^ instance ^ ".service-now.com/oauth_token.do")

val withToken = withToken
val scope = None
val nameForScopeParameter = None
val parseTokenResponse = None
val hosted_domain = None
end)

val token =
c <- getCookie user;
case c of
None => return None
| Some n =>
oneOrNoRowsE1 (SELECT (secrets.Token)
FROM secrets
WHERE secrets.Secret = {[n]}
AND secrets.Expires > CURRENT_TIMESTAMP)

val logout = clearCookie user

val status =
toko <- token;
li <- source (Option.isSome toko);
cur <- currentUrl;
return <xml>
<dyn signal={liV <- signal li;
if liV then
return <xml><button value="Log out of ServiceNow"
onclick={fn _ => rpc logout; set li False}/></xml>
else
return <xml><button value="Log into ServiceNow"
onclick={fn _ => redirect (url authorize)}/></xml>}/>
</xml>
end
28 changes: 28 additions & 0 deletions src/ur/serviceNow.urs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
structure Scope : sig
type t
val empty : t
val union : t -> t -> t
val readonly : t -> bool
end

signature AUTH = sig
val token : transaction (option string)
end

functor ThreeLegged(M : sig
val instance : string
val client_id : string
val client_secret : string
val https : bool

val scopes : Scope.t
val onCompletion : transaction page
end) : sig
val token : transaction (option string)
val authorize : transaction page
val status : transaction xbody
val logout : transaction unit
end

functor Make(M : AUTH) : sig
end

0 comments on commit b12db6a

Please sign in to comment.