1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
open! Values
open! Awso.Import
let sha1_insecure s =
(let h = Cryptokit.Hash.sha1 () in
let hh = Cryptokit.hash_string h s in
Cryptokit.transform_string (Cryptokit.Hexa.encode ()) hh)
[@alert "-crypto"]
;;
let format_utc (t : float) =
let tm = Unix.gmtime t in
sprintf
"%04d-%02d-%02d %02d:%02d:%02dZ"
(tm.tm_year + 1900)
(tm.tm_mon + 1)
tm.tm_mday
tm.tm_hour
tm.tm_min
tm.tm_sec
;;
let validate_expiration expiration =
let expiration =
expiration
|> Option.value_exn
~message:
"Sso.get_role_credentials returned roleCredentials with empty expiration??"
|> Int64.to_float
in
let now = Unix.gettimeofday () in
if Float.( < ) expiration now
then
failwithf
"Sso.get_role_credentials returned roleCredentials that were already expired! %s < \
now (%s)"
(format_utc expiration)
(format_utc now)
()
;;
let get_cached_sso_token_file_path ~cfg =
let fn =
match cfg.Awso.Cfg.aws_session_token with
| None | Some "" -> (
match cfg.Awso.Cfg.sso_start_url with
| None | Some "" -> None
| Some x -> Some x)
| Some x -> Some x
in
match fn with
| None ->
failwithf
"No cached SSO credentials found for URL %s; run `aws sso login`"
(cfg.Awso.Cfg.sso_start_url |> Option.value ~default:"<none>")
()
| Some fn ->
let sha1 = sha1_insecure fn in
sprintf "%s/.aws/sso/cache/%s.json" (Stdlib.Sys.getenv "HOME") sha1
;;
let get_sso_role_request_and_cfg_exn ~cfg ~cached_sso_token_file jsonstr =
let json = Yojson.Safe.from_string jsonstr in
let member_string member =
match Yojson.Safe.Util.member member json with
| `Null -> failwithf "No '%s' found in %s" member cached_sso_token_file ()
| `String access_token -> access_token
| _ ->
failwithf
"'%s' from %s has unexpected type; wanted string"
member
cached_sso_token_file
()
in
let request =
GetRoleCredentialsRequest.make
~accountId:
(cfg.Awso.Cfg.sso_account_id
|> Option.value_exn ~message:"No 'sso_account_id' set in credentials")
~accessToken:(member_string "accessToken")
~roleName:
(cfg.Awso.Cfg.sso_role_name
|> Option.value_exn ~message:"No 'sso_role_name' set in credentials")
()
in
let sso_region = member_string "region" |> Awso.Region.of_string in
let cfg = { cfg with region = Some sso_region } in
request, cfg
;;
let get_sso_role_request_and_cfg ~cfg ~cached_sso_token_file jsonstr =
Result.try_with (fun () ->
get_sso_role_request_and_cfg_exn ~cfg ~cached_sso_token_file jsonstr)
;;
let parse_role_credentials_response_exn = function
| Ok ({ roleCredentials } : GetRoleCredentialsResponse.t) -> roleCredentials
| Error (`UnauthorizedException (ue : UnauthorizedException.t)) ->
eprintf "Unauthorized: %s\n" (Option.value ~default:"<no message given>" ue.message);
eprintf "Maybe you need to re-run `aws sso login`?\n";
exit 1
| Error err ->
failwithf
"Sso.get_role_credentials: AWS call: %s"
(err |> GetRoleCredentialsResponse.error_to_json |> Yojson.Safe.to_string)
()
;;
let update_cfg_with_role_credentials_exn ~(cfg : Awso.Cfg.t) role_credentials =
let ({ accessKeyId; secretAccessKey; sessionToken; expiration } : RoleCredentials.t) =
role_credentials
|> Option.value_exn
~message:"Sso.get_role_credentials returned empty roleCredentials??"
in
let () = validate_expiration expiration in
let not_empty s v =
match v with
| None | Some "" -> failwithf "Sso.get_role_credentials returned empty %s" s ()
| Some _ -> v
in
{ cfg with
aws_access_key_id = not_empty "accessKeyId" accessKeyId
; aws_secret_access_key = not_empty "secretAccessKey" secretAccessKey
; aws_session_token = not_empty "sessionToken" sessionToken
}
;;
let update_cfg_with_role_credentials ~(cfg : Awso.Cfg.t) role_credentials =
Result.try_with (fun () -> update_cfg_with_role_credentials_exn ~cfg role_credentials)
;;