Skip to content

Commit 45529b4

Browse files
committed
Merge commit '277b642da278e484b0b983eea38ee1051feff13e' into feature/fix-friendlyname
2 parents 5cef508 + 277b642 commit 45529b4

File tree

7 files changed

+116
-16
lines changed

7 files changed

+116
-16
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# This workflow will install Python dependencies, run tests and lint with a variety of Python versions
2+
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
3+
4+
name: Python package
5+
6+
on: [push, pull_request]
7+
8+
jobs:
9+
test:
10+
runs-on: ubuntu-latest
11+
strategy:
12+
fail-fast: false
13+
matrix:
14+
python-version: [2.7,3.5,3.6,3.7, 3.8,3.9]
15+
16+
steps:
17+
- uses: actions/checkout@v2
18+
- name: Set up Python ${{ matrix.python-version }}
19+
uses: actions/setup-python@v2
20+
with:
21+
python-version: ${{ matrix.python-version }}
22+
23+
- name: Install dependencies
24+
run: |
25+
sudo apt-get update -qq
26+
sudo apt-get install -qq swig python-dev libxml2-dev libxmlsec1-dev
27+
make install-req
28+
make install-test
29+
30+
- name: Test
31+
run: |
32+
make pytest
33+
make pycodestyle
34+
make flake8
35+

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,15 @@ __pycache_
1414
/*.eg
1515
*.egg-info
1616
/eggs
17+
/.eggs
1718
/build
1819
/dist
1920
/venv
2021
.coverage
2122
.pypirc
2223
/.idea
2324
.mypy_cache/
25+
.pytest_cache
2426

2527
*.key
2628
*.crt

Makefile

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
PIP=pip
2+
FLAKE8=flake8
3+
PYTEST=pytest
4+
PYCODESTYLE=pycodestyle
5+
COVERAGE=coverage
6+
COVERAGE_CONFIG=tests/coverage.rc
7+
PEP8_CONFIG=tests/pep8.rc
8+
MAIN_SOURCE=src/onelogin/saml2
9+
DEMOS=demo-django demo-flask
10+
TESTS=tests/src/OneLogin/saml2_tests
11+
SOURCES=$(MAIN_SOURCE) $(DEMO) $(TESTS)
12+
13+
install-req:
14+
$(PIP) install --upgrade 'setuptools<45.0.0'
15+
$(PIP) install .
16+
17+
install-test:
18+
$(PIP) install -e ".[test]"
19+
20+
pytest:
21+
$(COVERAGE) run --source $(MAIN_SOURCE) --rcfile=$(COVERAGE_CONFIG) -m pytest
22+
$(COVERAGE) report -m --rcfile=$(COVERAGE_CONFIG)
23+
24+
pycodestyle:
25+
$(PYCODESTYLE) --ignore=E501,E731,W504 $(SOURCES) --config=$(PEP8_CONFIG)
26+
27+
flake8:
28+
$(FLAKE8) --ignore=E501,E731,W504 $(SOURCES)
29+
30+
clean:
31+
rm -rf .pytest_cache/
32+
rm -rf .eggs/
33+
find . -type d -name "__pycache__" -exec rm -r {} +
34+
find . -type d -name "*.egg-info" -exec rm -r {} +
35+
rm .coverage

README.md

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -522,6 +522,13 @@ There's an easier method -- use a metadata exchange. Metadata is just an XML fi
522522

523523
Using ````parse_remote```` IdP metadata can be obtained and added to the settings without further ado.
524524

525+
Take in mind that the OneLogin_Saml2_IdPMetadataParser class does not validate in any way the URL that is introduced in order to be parsed.
526+
527+
Usually the same administrator that handles the Service Provider also sets the URL to the IdP, which should be a trusted resource.
528+
529+
But there are other scenarios, like a SAAS app where the administrator of the app delegates this functionality to other users. In this case, extra precaution should be taken in order to validate such URL inputs and avoid attacks like SSRF.
530+
531+
525532
``
526533
idp_data = OneLogin_Saml2_IdPMetadataParser.parse_remote('https://example.com/auth/saml2/idp/metadata')
527534
``
@@ -568,9 +575,10 @@ req = {
568575

569576
# Advanced request options
570577
"https": "",
571-
"lowercase_urlencoding": "",
572578
"request_uri": "",
573-
"query_string": ""
579+
"query_string": "",
580+
"validate_signature_from_qs": False,
581+
"lowercase_urlencoding": False
574582
}
575583
```
576584

@@ -602,12 +610,12 @@ An explanation of some advanced request parameters:
602610

603611
* `https` - Defaults to ``off``. Set this to ``on`` if you receive responses over HTTPS.
604612

605-
* `lowercase_urlencoding` - Defaults to `false`. ADFS users should set this to `true`.
606-
607-
* `request_uri` - The path where your SAML server recieves requests. Set this if requests are not recieved at the server's root.
613+
* `request_uri` - The path where your SAML server receives requests. Set this if requests are not received at the server's root.
608614

609615
* `query_string` - Set this with additional query parameters that should be passed to the request endpoint.
610616

617+
* `validate_signature_from_qs` - If `True`, use `query_string` to validate request and response signatures. Otherwise, use `get_data`. Defaults to `False`. Note that when using `get_data`, query parameters need to be url-encoded for validation. By default we use upper-case url-encoding. Some IdPs, notably Microsoft AD, use lower-case url-encoding, which makes signature validation to fail. To fix this issue, either pass `query_string` and set `validate_signature_from_qs` to `True`, which works for all IdPs, or set `lowercase_urlencoding` to `True`, which only works for AD.
618+
611619

612620
#### Initiate SSO ####
613621

setup.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@
4949
'freezegun>=0.3.11, <=1.1.0',
5050
'pylint==1.9.4',
5151
'flake8>=3.6.0',
52-
'coveralls==1.5.1',
52+
'coveralls>=1.11.1',
53+
'pytest>=4.6',
5354
),
5455
},
5556
keywords='saml saml2 xmlsec django flask pyramid python3',

src/onelogin/saml2/auth.py

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,22 @@ def add_response_signature(self, response_data, sign_algorithm=OneLogin_Saml2_Co
528528
"""
529529
return self.__build_signature(response_data, 'SAMLResponse', sign_algorithm)
530530

531+
@staticmethod
532+
def __build_sign_query_from_qs(query_string, saml_type):
533+
"""
534+
Build sign query from query string
535+
536+
:param query_string: The query string
537+
:type query_string: str
538+
539+
:param saml_type: The target URL the user should be redirected to
540+
:type saml_type: string SAMLRequest | SAMLResponse
541+
"""
542+
args = ('%s=' % saml_type, 'RelayState=', 'SigAlg=')
543+
parts = query_string.split('&')
544+
# Join in the order of arguments rather than the original order of parts.
545+
return '&'.join(part for arg in args for part in parts if part.startswith(arg))
546+
531547
@staticmethod
532548
def __build_sign_query(saml_data, relay_state, algorithm, saml_type, lowercase_urlencoding=False):
533549
"""
@@ -660,16 +676,16 @@ def __validate_signature(self, data, saml_type, raise_exceptions=False):
660676
if isinstance(sign_alg, bytes):
661677
sign_alg = sign_alg.decode('utf8')
662678

663-
lowercase_urlencoding = False
664-
if 'lowercase_urlencoding' in self.__request_data.keys():
665-
lowercase_urlencoding = self.__request_data['lowercase_urlencoding']
666-
667-
signed_query = self.__build_sign_query(data[saml_type],
668-
data.get('RelayState', None),
669-
sign_alg,
670-
saml_type,
671-
lowercase_urlencoding
672-
)
679+
query_string = self.__request_data.get('query_string')
680+
if query_string and self.__request_data.get('validate_signature_from_qs'):
681+
signed_query = self.__build_sign_query_from_qs(query_string, saml_type)
682+
else:
683+
lowercase_urlencoding = self.__request_data.get('lowercase_urlencoding', False)
684+
signed_query = self.__build_sign_query(data[saml_type],
685+
data.get('RelayState'),
686+
sign_alg,
687+
saml_type,
688+
lowercase_urlencoding)
673689

674690
if exists_multix509sign:
675691
for cert in idp_data['x509certMulti']['signing']:

src/onelogin/saml2/idp_metadata_parser.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@
2323
class OneLogin_Saml2_IdPMetadataParser(object):
2424
"""
2525
A class that contain methods related to obtaining and parsing metadata from IdP
26+
27+
This class does not validate in any way the URL that is introduced,
28+
make sure to validate it properly before use it in a get_metadata method.
2629
"""
2730

2831
@classmethod

0 commit comments

Comments
 (0)