-
Notifications
You must be signed in to change notification settings - Fork 3
/
saml.lua
executable file
·149 lines (129 loc) · 4.55 KB
/
saml.lua
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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
local cjson = require("cjson")
local inspect = require("inspect")
local ngx_var = ngx.var
local saml = {}
local secret = os.getenv("SESSION_SECRET") or "secret123"
local profileLocation = os.getenv("PROFILE_LOCATION")
local logoutLocation = os.getenv("LOGOUT_LOCATION")
-- call with access_by_lua 'require("saml").checkAccess()';
function saml.checkAccess()
local session = require "resty.session".open{ secret = secret }
-- no session, redirect to idp
if not session.data.nameID then
local relay_state = ngx_var.scheme.."://"..ngx.req.get_headers().host..ngx_var.request_uri
local redirect_url = ngx_var.saml_idp_url.."?RelayState="..relay_state
-- ngx.log(ngx.ERR, "redirect to idp:"..redirect_url)
return ngx.redirect(redirect_url)
end
-- enrich request with session data
for key, value in pairs(session.data) do
ngx.req.set_header(key, value)
end
end
-- called by IdP after login, POST SAMLResponse and RelayState
function saml.acs()
ngx.req.read_body()
ngx.log(ngx.INFO, ngx.var.request_body)
local http = require "resty.http"
local httpc = http.new()
local res, err = httpc:request_uri("http://127.0.0.1:3000/saml/validatePostResponse",{
method = "POST",
body = ngx.var.request_body,
headers = {
["Content-Type"] = "application/x-www-form-urlencoded"
}
})
httpc:set_keepalive()
-- ngx.log(ngx.INFO, "res status: ", res.status)
-- ngx.log(ngx.INFO, "res.headers: ", inspect(res))
if res.status ~= 200 then
ngx.log(ngx.INFO, "SAML Auth Failed")
ngx.exit(403)
end
-- start session
local session = require "resty.session".start{ secret = secret }
-- store assertions
local assertions = cjson.decode(res.body)
-- ngx.log(ngx.INFO, inspect(assertions))
for key, val in pairs(assertions) do
session.data[ngx.escape_uri(key)] = val
end
-- load external session data
if profileLocation then
-- enrich request with session data
for key, value in pairs(session.data) do
ngx.req.set_header(key, value)
end
-- load profile
local res = ngx.location.capture(profileLocation)
if res.status == 200 then
local profile = cjson.decode(res.body)
-- ngx.log(ngx.INFO, inspect(profile))
for key, val in pairs(profile) do
session.data[ngx.escape_uri(key)] = val
end
-- set cookie if any
if res.header['Set-Cookie'] then
ngx.header['Set-Cookie'] = res.header['Set-Cookie']
ngx.log(ngx.INFO, session.data.nameID..": Set-Cookie: "..res.header['Set-Cookie'])
end
else
-- problem with profile, clean up and exit
ngx.log(ngx.ERR, "Error loading profile for: "..session.data.nameID.." status:"..res.status)
session:destroy()
ngx.exit(403)
end
end
-- session is good
session:save()
ngx.log(ngx.INFO, "login: "..session.data.nameID)
-- redirect to RelayState
local args, err = ngx.req.get_post_args()
local relayState = args.RelayState
if (relayState ~= nill and relayState ~= "") then
return ngx.redirect(relayState)
else
return ngx.exit(200)
end
end
-- destroy session
function saml.logout()
local session = require "resty.session".open{ secret = secret }
if session.data.nameID then
ngx.log(ngx.INFO, "logout: "..session.data.nameID)
end
-- call logout url if set
if logoutLocation then
-- enrich request with session data
for key, value in pairs(session.data) do
ngx.req.set_header(key, value)
end
local res = ngx.location.capture(logoutLocation)
ngx.log(ngx.INFO, logoutLocation.." "..res.status)
end
-- clear all session* cookies
local ck = require "resty.cookie"
local cookie, err = ck:new()
if not cookie then
ngx.log(ngx.ERR, err)
end
if cookie then
local fields, err = cookie:get_all()
if fields then
for k, v in pairs(fields) do
if ngx.re.find(k, "^session.+") then
ngx.log(ngx.INFO, "deleting cookie: "..k)
cookie:set({
key = k,
value = "",
path = "/",
expires = "Thu, Jan 01 1970 00:00:00 UTC"
})
end
end
end
end
session:destroy()
return ngx.exit(200)
end
return saml