Skip to content

Commit a9bbd80

Browse files
committed
Fixed #16
1 parent f56cd68 commit a9bbd80

File tree

7 files changed

+114
-20
lines changed

7 files changed

+114
-20
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# CHANGELOG
22

3+
### v0.9.1
4+
5+
+ Remove the need for supplying certicate and key files if the requests are
6+
not signed (Issue #16). Useful during development when the corresponding
7+
Identity Provider is setup for unsigned requests/responses. Use signing
8+
for production deployments. The defaults expect signed requests/responses.
9+
310
### v0.9.0
411

512
+ Issue: #12. Support for IDP initiated SSO flow.

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -179,8 +179,8 @@ config :samly, Samly.Provider,
179179
| **Service Provider Parameters** | |
180180
| `id` | _(mandatory)_ |
181181
| `identity_id` | _(optional)_ If omitted, the metadata URL will be used |
182-
| `certfile` | _(optional)_ Defaults to "samly.crt" |
183-
| `keyfile` | _(optional)_ Defaults to "samly.pem" |
182+
| `certfile` | _(optional)_ This is needed when SAML requests/responses need to be signed. Make sure to set this in a production deployment. Could be omitted during development if your IDP is setup to not require signing. If that is the case, the following **Identity Provider Parameters** must be explicitly set to false: `sign_requests`, `sign_metadata`, `signed_assertion_in_resp`, `signed_envelopes_in_resp`|
183+
| `keyfile` | _(optional)_ Similar to `certfile` |
184184
| `contact_name` | _(optional)_ Technical contact name for the Service Provider |
185185
| `contact_email` | _(optional)_ Technical contact email address |
186186
| `org_name` | _(optional)_ SAML Service Provider (your app) Organization name |
@@ -193,7 +193,7 @@ config :samly, Samly.Provider,
193193
| `metadata_file` | _(mandatory)_ Path to the IdP metadata XML file obtained from the Identity Provider. |
194194
| `pre_session_create_pipeline` | _(optional)_ Check the customization section. |
195195
| `use_redirect_for_req` | _(optional)_ Default is `false`. When this is `false`, `Samly` will POST to the IdP SAML endpoints. |
196-
| `signed_requests`, `signed_metadata` | _(optional)_ Default is `true`. |
196+
| `sign_requests`, `sign_metadata` | _(optional)_ Default is `true`. |
197197
| `signed_assertion_in_resp`, `signed_envelopes_in_resp` | _(optional)_ Default is `true`. When `true`, `Samly` expects the requests and responses from IdP to be signed. |
198198
| `allow_idp_initiated_flow` | _(optional)_ Default is `false`. IDP initiated SSO is allowed only when this is set to `true`. |
199199
| `allowed_target_urls` | _(optional)_ Default is `[]`. `Samly` uses this **only** when `allow_idp_initiated_flow` parameter is set to `true`. Make sure to set this to one or more exact URLs you want to allow (whitelist). The URL to redirect the user after completing the SSO flow is sent from IDP in auth response as `relay_state`. This `relay_state` target URL is matched against this URL list. Set the value to `nil` if you do not want this whitelist capability. |

lib/samly/idp_data.ex

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,14 +140,28 @@ defmodule Samly.IdpData do
140140
%SpData{} = sp ->
141141
idp_data = %IdpData{idp_data | esaml_idp_rec: to_esaml_idp_metadata(idp_data, opts_map)}
142142
idp_data = %IdpData{idp_data | esaml_sp_rec: get_esaml_sp(sp, idp_data)}
143-
%IdpData{idp_data | valid?: true}
143+
%IdpData{idp_data | valid?: cert_config_ok?(idp_data, sp)}
144144

145145
_ ->
146146
Logger.error("[Samly] Unknown/invalid sp_id: #{idp_data.sp_id}")
147147
idp_data
148148
end
149149
end
150150

151+
@spec cert_config_ok?(%IdpData{}, %SpData{}) :: boolean
152+
defp cert_config_ok?(%IdpData{} = idp_data, %SpData{} = sp_data) do
153+
if (idp_data.sign_metadata ||
154+
idp_data.sign_requests ||
155+
idp_data.signed_assertion_in_resp ||
156+
idp_data.signed_envelopes_in_resp) &&
157+
(sp_data.cert == :undefined || sp_data.key == :undefined) do
158+
Logger.error("[Samly] SP cert or key missing - Skipping identity provider: #{idp_data.id}")
159+
false
160+
else
161+
true
162+
end
163+
end
164+
151165
@default_metadata_file "idp_metadata.xml"
152166

153167
@spec set_metadata_file(%IdpData{}, map()) :: %IdpData{}

lib/samly/sp_data.ex

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,9 @@ defmodule Samly.SpData do
8080
end
8181

8282
@spec load_cert(%SpData{}, map()) :: %SpData{}
83+
defp load_cert(%SpData{certfile: ""} = sp_data, _) do
84+
%SpData{sp_data | cert: :undefined}
85+
end
8386
defp load_cert(%SpData{certfile: certfile} = sp_data, %{} = opts_map) do
8487
try do
8588
cert = :esaml_util.load_certificate(certfile)
@@ -92,6 +95,9 @@ defmodule Samly.SpData do
9295
end
9396

9497
@spec load_key(%SpData{}, map()) :: %SpData{}
98+
defp load_key(%SpData{keyfile: ""} = sp_data, _) do
99+
%SpData{sp_data | key: :undefined}
100+
end
95101
defp load_key(%SpData{keyfile: keyfile} = sp_data, %{} = opts_map) do
96102
try do
97103
key = :esaml_util.load_private_key(keyfile)

mix.exs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
defmodule Samly.Mixfile do
22
use Mix.Project
33

4-
@version "0.9.0"
4+
@version "0.9.1"
55
@description "SAML SP SSO made easy"
66
@source_url "https://github.com/handnot2/samly"
77

test/samly_idp_data_test.exs

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,20 @@ defmodule SamlyIdpDataTest do
1616
keyfile: "test/data/test.pem"
1717
}
1818

19+
@sp_config3 %{
20+
id: "sp3",
21+
keyfile: "test/data/test.pem"
22+
}
23+
24+
@sp_config4 %{
25+
id: "sp4",
26+
certfile: "test/data/test.crt"
27+
}
28+
29+
@sp_config5 %{
30+
id: "sp5"
31+
}
32+
1933
@idp_config1 %{
2034
id: "idp1",
2135
sp_id: "sp1",
@@ -33,7 +47,15 @@ defmodule SamlyIdpDataTest do
3347
setup context do
3448
sp_data1 = SpData.load_provider(@sp_config1)
3549
sp_data2 = SpData.load_provider(@sp_config2)
36-
[sps: %{sp_data1.id => sp_data1, sp_data2.id => sp_data2}] |> Enum.into(context)
50+
sp_data3 = SpData.load_provider(@sp_config3)
51+
sp_data4 = SpData.load_provider(@sp_config4)
52+
sp_data5 = SpData.load_provider(@sp_config5)
53+
[sps: %{
54+
sp_data1.id => sp_data1,
55+
sp_data2.id => sp_data2,
56+
sp_data3.id => sp_data3,
57+
sp_data4.id => sp_data4,
58+
sp_data5.id => sp_data5}] |> Enum.into(context)
3759
end
3860

3961
test "valid-idp-config-1", %{sps: sps} do
@@ -179,4 +201,49 @@ defmodule SamlyIdpDataTest do
179201
%IdpData{} = idp_data = IdpData.load_provider(idp_config, sps)
180202
refute idp_data.valid?
181203
end
204+
205+
test "valid-idp-config-signing-turned-off", %{sps: sps} do
206+
idp_config =
207+
Map.merge(@idp_config1, %{
208+
sp_id: "sp5",
209+
use_redirect_for_req: true,
210+
sign_requests: false,
211+
sign_metadata: false,
212+
signed_assertion_in_resp: false,
213+
signed_envelopes_in_resp: false
214+
})
215+
216+
%IdpData{} = idp_data = IdpData.load_provider(idp_config, sps)
217+
assert idp_data.valid?
218+
end
219+
220+
test "invalid-idp-config-signing-on-cert-missing", %{sps: sps} do
221+
idp_config =
222+
Map.merge(@idp_config1, %{
223+
sp_id: "sp3",
224+
use_redirect_for_req: true,
225+
sign_requests: true,
226+
sign_metadata: false,
227+
signed_assertion_in_resp: false,
228+
signed_envelopes_in_resp: false
229+
})
230+
231+
%IdpData{} = idp_data = IdpData.load_provider(idp_config, sps)
232+
refute idp_data.valid?
233+
end
234+
235+
test "invalid-idp-config-signing-on-key-missing", %{sps: sps} do
236+
idp_config =
237+
Map.merge(@idp_config1, %{
238+
sp_id: "sp4",
239+
use_redirect_for_req: true,
240+
sign_requests: true,
241+
sign_metadata: false,
242+
signed_assertion_in_resp: false,
243+
signed_envelopes_in_resp: false
244+
})
245+
246+
%IdpData{} = idp_data = IdpData.load_provider(idp_config, sps)
247+
refute idp_data.valid?
248+
end
182249
end

test/samly_sp_data_test.exs

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,44 +14,44 @@ defmodule SamlySpDataTest do
1414
assert sp_data.valid?
1515
end
1616

17-
test "invalid-sp-config-1" do
18-
sp_config = %{@sp_config1 | id: ""}
19-
%SpData{} = sp_data = SpData.load_provider(sp_config)
20-
refute sp_data.valid?
21-
end
22-
23-
test "invalid-sp-config-2" do
17+
test "cert-unspecified-sp-config" do
2418
sp_config = %{@sp_config1 | certfile: ""}
25-
%SpData{} = sp_data = SpData.load_provider(sp_config)
26-
refute sp_data.valid?
19+
%SpData{cert: :undefined} = sp_data = SpData.load_provider(sp_config)
20+
assert sp_data.valid?
2721
end
2822

29-
test "invalid-sp-config-3" do
23+
test "key-unspecified-sp-config" do
3024
sp_config = %{@sp_config1 | keyfile: ""}
25+
%SpData{key: :undefined} = sp_data = SpData.load_provider(sp_config)
26+
assert sp_data.valid?
27+
end
28+
29+
test "invalid-sp-config-1" do
30+
sp_config = %{@sp_config1 | id: ""}
3131
%SpData{} = sp_data = SpData.load_provider(sp_config)
3232
refute sp_data.valid?
3333
end
3434

35-
test "invalid-sp-config-4" do
35+
test "invalid-sp-config-2" do
3636
sp_config = %{@sp_config1 | certfile: "non-existent.crt"}
3737
%SpData{} = sp_data = SpData.load_provider(sp_config)
3838
refute sp_data.valid?
3939
end
4040

41-
test "invalid-sp-config-5" do
41+
test "invalid-sp-config-3" do
4242
sp_config = %{@sp_config1 | keyfile: "non-existent.pem"}
4343
%SpData{} = sp_data = SpData.load_provider(sp_config)
4444
refute sp_data.valid?
4545
end
4646

47-
test "invalid-sp-config-6" do
47+
test "invalid-sp-config-4" do
4848
sp_config = %{@sp_config1 | certfile: "test/data/test.pem"}
4949
%SpData{} = sp_data = SpData.load_provider(sp_config)
5050
refute sp_data.valid?
5151
end
5252

5353
@tag :skip
54-
test "invalid-sp-config-7" do
54+
test "invalid-sp-config-5" do
5555
sp_config = %{@sp_config1 | keyfile: "test/data/test.crt"}
5656
%SpData{} = sp_data = SpData.load_provider(sp_config)
5757
refute sp_data.valid?

0 commit comments

Comments
 (0)