Skip to content
This repository was archived by the owner on Jun 1, 2023. It is now read-only.

Commit fc22fc8

Browse files
authored
Merge pull request #27 from IdentityPython/develop
Merging develop into master
2 parents 0f7f96f + 5f3fdf7 commit fc22fc8

File tree

9 files changed

+135
-70
lines changed

9 files changed

+135
-70
lines changed

.travis.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ python:
99
addons:
1010
apt:
1111
packages:
12-
-
12+
- rustc
13+
- cargo
1314
install:
1415
- pip install codecov
1516
- pip install isort

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,10 @@ test:
4040
.PHONY: test
4141

4242
isort:
43-
@pipenv run isort --recursive $(OICDIR) $(TESTDIR)
43+
@pipenv run isort $(OICDIR) $(TESTDIR)
4444

4545
check-isort:
46-
@pipenv run isort --recursive --diff --check-only $(OICDIR) $(TESTDIR)
46+
@pipenv run isort --diff --check-only $(OICDIR) $(TESTDIR)
4747
.PHONY: isort check-isort
4848

4949
check-pylama:

src/oidcmsg/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
__author__ = 'Roland Hedberg'
2-
__version__ = '1.1.2'
2+
__version__ = '1.1.4'
3+
4+
import os
35

46
VERIFIED_CLAIM_PREFIX = '__verified'
57

src/oidcmsg/oidc/__init__.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -824,6 +824,11 @@ def verify(self, **kwargs):
824824
else:
825825
if (_iat + _storage_time) < (_now - _skew):
826826
raise IATError('Issued too long ago')
827+
elif _iat > _now + _skew:
828+
raise IATError('Issued sometime in the future')
829+
830+
if _exp < _iat:
831+
raise IATError('Expiration time can not be earlier the issued at')
827832

828833
if 'nonce' in kwargs and 'nonce' in self:
829834
if kwargs['nonce'] != self['nonce']:
@@ -925,6 +930,14 @@ def verify(self, **kwargs):
925930
elif parts.scheme != "https":
926931
raise SchemeError("Not HTTPS")
927932

933+
# The parameter is optional
934+
if "token_endpoint_auth_signing_alg_values_supported" in self and "none" in self[
935+
"token_endpoint_auth_signing_alg_values_supported"]:
936+
raise ValueError(
937+
"The value none must not be used for "
938+
"token_endpoint_auth_signing_alg_values_supported"
939+
)
940+
928941
if "RS256" not in self["id_token_signing_alg_values_supported"]:
929942
raise ValueError('RS256 missing from id_token_signing_alg_values_supported')
930943

@@ -934,7 +947,7 @@ def verify(self, **kwargs):
934947
raise ValueError('Issuer ID invalid')
935948

936949
if any("code" in rt for rt in self[
937-
"response_types_supported"]) and "token_endpoint" not in self:
950+
"response_types_supported"]) and "token_endpoint" not in self:
938951
raise MissingRequiredAttribute("token_endpoint")
939952

940953
return True

src/oidcmsg/oidc/session.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from cryptojwt.exception import UnsupportedAlgorithm
44

5+
from oidcmsg.message import OPTIONAL_LIST_OF_SP_SEP_STRINGS
56
from oidcmsg.time_util import utc_time_sans_frac
67

78
from ..exception import MessageException
@@ -50,7 +51,8 @@ class EndSessionRequest(Message):
5051
c_param = {
5152
"id_token_hint": SINGLE_OPTIONAL_IDTOKEN,
5253
"post_logout_redirect_uri": SINGLE_OPTIONAL_STRING,
53-
"state": SINGLE_OPTIONAL_STRING
54+
"state": SINGLE_OPTIONAL_STRING,
55+
"ui_locales": OPTIONAL_LIST_OF_SP_SEP_STRINGS
5456
}
5557

5658
def verify(self, **kwargs):

src/oidcmsg/storage/abfile.py

Lines changed: 27 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -87,14 +87,16 @@ def __getitem__(self, item):
8787
:return:
8888
"""
8989
item = self.key_conv.serialize(item)
90-
91-
if self.is_changed(item):
92-
logger.info("File content change in {}".format(item))
93-
fname = os.path.join(self.fdir, item)
94-
self.storage[item] = self._read_info(fname)
95-
96-
logger.debug('Read from "%s"', item)
97-
return self.storage[item]
90+
if self._is_file(item):
91+
if self.is_changed(item):
92+
logger.info("File content change in {}".format(item))
93+
fname = os.path.join(self.fdir, item)
94+
self.storage[item] = self._read_info(fname)
95+
96+
logger.debug('Read from "%s"', item)
97+
return self.storage[item]
98+
else:
99+
raise KeyError(item)
98100

99101
def __setitem__(self, key, value):
100102
"""
@@ -163,31 +165,32 @@ def get_mtime(fname):
163165

164166
return mtime
165167

168+
def _is_file(self, item):
169+
fname = os.path.join(self.fdir, item)
170+
return os.path.isfile(fname)
171+
166172
def is_changed(self, item):
167173
"""
168-
Find out if this item has been modified since last
174+
Find out if this item has been modified since last.
175+
When I get here I know that item points to an existing file.
169176
170177
:param item: A key
171178
:return: True/False
172179
"""
173180
fname = os.path.join(self.fdir, item)
174-
if os.path.isfile(fname):
175-
mtime = self.get_mtime(fname)
181+
mtime = self.get_mtime(fname)
176182

177-
try:
178-
_ftime = self.fmtime[item]
179-
except KeyError: # Never been seen before
180-
self.fmtime[item] = mtime
181-
return True
182-
183-
if mtime > _ftime: # has changed
184-
self.fmtime[item] = mtime
185-
return True
186-
else:
187-
return False
183+
try:
184+
_ftime = self.fmtime[item]
185+
except KeyError: # Never been seen before
186+
self.fmtime[item] = mtime
187+
return True
188+
189+
if mtime > _ftime: # has changed
190+
self.fmtime[item] = mtime
191+
return True
188192
else:
189-
logger.error('Could not access {}'.format(fname))
190-
raise KeyError(item)
193+
return False
191194

192195
def _read_info(self, fname):
193196
if os.path.isfile(fname):

src/oidcmsg/storage/absqlalchemy.py

Lines changed: 18 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -2,77 +2,54 @@
22
import json
33

44
import sqlalchemy as alchemy_db
5+
from sqlalchemy.orm import scoped_session
56
from sqlalchemy.orm import sessionmaker
67

7-
from oidcmsg.storage.db_setup import Base
8-
8+
PlainDict = dict
99

1010
class AbstractStorageSQLAlchemy:
1111
def __init__(self, conf_dict):
1212
self.engine = alchemy_db.create_engine(conf_dict['url'])
1313
self.connection = self.engine.connect()
14-
Base.metadata.create_all(self.engine)
1514
self.metadata = alchemy_db.MetaData()
1615
self.table = alchemy_db.Table(conf_dict['params']['table'],
1716
self.metadata, autoload=True,
1817
autoload_with=self.engine)
1918
Session = sessionmaker(bind=self.engine)
20-
self.session = Session()
19+
self.session = scoped_session(Session)
2120

2221
def get(self, k):
23-
entries = self.session.query(self.table).filter_by(owner=k).all()
24-
result = self._data_from_db(entries)
25-
return result
26-
27-
def _data_from_db(self, entries):
28-
result = []
29-
for entry in entries:
30-
try:
31-
data = json.loads(entry.data)
32-
if isinstance(data, list):
33-
result.extend(data)
34-
else:
35-
result.append(data)
36-
except:
37-
result.append(entry.data)
38-
return result
39-
40-
def _data_to_db(self, v):
41-
if isinstance(v, dict) or isinstance(v, list):
42-
value = json.dumps(v)
43-
else:
44-
value = v
45-
return value
22+
entry = self.session.query(self.table).filter_by(owner=k).first()
23+
if entry is None:
24+
return None
25+
return entry.data
4626

4727
def set(self, k, v):
48-
value = self._data_to_db(v)
28+
self.delete(k)
4929
ins = self.table.insert().values(owner=k,
50-
data=value)
30+
data=v)
5131
self.session.execute(ins)
5232
self.session.commit()
5333
return 1
5434

55-
def update(self, k, v, col_match='owner', col_value='data'):
35+
def update(self, k, v):
5636
"""
5737
k = value_to_match
5838
v = value_to_be_substituted
5939
"""
60-
value = self._data_to_db(v)
61-
table_column = getattr(self.table.c, col_match)
6240
upquery = self.table.update(). \
63-
where(table_column == k). \
64-
values(**{col_value: value})
41+
where(self.table.c.owner == k). \
42+
values(**{'data': v})
6543
self.session.execute(upquery)
6644
self.session.commit()
6745
return 1
6846

69-
def delete(self, v, k='owner'):
47+
def delete(self, v):
7048
"""
7149
return the count of deleted objects
7250
"""
73-
table_column = getattr(self.table.c, k)
74-
delquery = self.table.delete().where(table_column == v)
75-
n_entries = self.session.query(self.table).filter(table_column == v).count()
51+
delquery = self.table.delete().where(self.table.c.owner == v)
52+
n_entries = self.session.query(self.table).filter(self.table.c.owner == v).count()
7653
self.session.execute(delquery)
7754
return n_entries
7855

@@ -108,3 +85,6 @@ def flush(self):
10885
except:
10986
self.session.rollback()
11087
self.session.flush()
88+
89+
def __setitem__(self, k, v):
90+
return self.set(k, v)

tests/test_06_oidc.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@
3535
from oidcmsg.oidc import CHashError
3636
from oidcmsg.oidc import Claims
3737
from oidcmsg.oidc import DiscoveryRequest
38+
from oidcmsg.oidc import EXPError
39+
from oidcmsg.oidc import IATError
3840
from oidcmsg.oidc import IdToken
3941
from oidcmsg.oidc import Link
4042
from oidcmsg.oidc import OpenIDSchema
@@ -929,6 +931,68 @@ def test_id_token():
929931
idt.verify()
930932

931933

934+
def test_id_token_expired():
935+
_now = time_util.utc_time_sans_frac()
936+
937+
idt = IdToken(**{
938+
"sub": "553df2bcf909104751cfd8b2",
939+
"aud": [
940+
"5542958437706128204e0000",
941+
"554295ce3770612820620000"
942+
],
943+
"auth_time": 1441364872,
944+
"azp": "554295ce3770612820620000",
945+
"at_hash": "L4Ign7TCAD_EppRbHAuCyw",
946+
"iat": _now - 200,
947+
"exp": _now - 100,
948+
"iss": "https://sso.qa.7pass.ctf.prosiebensat1.com"
949+
})
950+
951+
with pytest.raises(EXPError):
952+
idt.verify()
953+
954+
955+
def test_id_token_iat_in_the_future():
956+
_now = time_util.utc_time_sans_frac()
957+
958+
idt = IdToken(**{
959+
"sub": "553df2bcf909104751cfd8b2",
960+
"aud": [
961+
"5542958437706128204e0000",
962+
"554295ce3770612820620000"
963+
],
964+
"auth_time": 1441364872,
965+
"azp": "554295ce3770612820620000",
966+
"at_hash": "L4Ign7TCAD_EppRbHAuCyw",
967+
"iat": _now + 600,
968+
"exp": _now + 1200,
969+
"iss": "https://sso.qa.7pass.ctf.prosiebensat1.com"
970+
})
971+
972+
with pytest.raises(IATError):
973+
idt.verify()
974+
975+
976+
def test_id_token_exp_before_iat():
977+
_now = time_util.utc_time_sans_frac()
978+
979+
idt = IdToken(**{
980+
"sub": "553df2bcf909104751cfd8b2",
981+
"aud": [
982+
"5542958437706128204e0000",
983+
"554295ce3770612820620000"
984+
],
985+
"auth_time": 1441364872,
986+
"azp": "554295ce3770612820620000",
987+
"at_hash": "L4Ign7TCAD_EppRbHAuCyw",
988+
"iat": _now + 50,
989+
"exp": _now,
990+
"iss": "https://sso.qa.7pass.ctf.prosiebensat1.com"
991+
})
992+
993+
with pytest.raises(IATError):
994+
idt.verify(skew=100)
995+
932996
class TestAccessTokenRequest(object):
933997
def test_example(self):
934998
_txt = 'grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA' \

tox.ini

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ envlist = py{36,37,38},docs,quality
44
[testenv]
55
passenv = CI TRAVIS TRAVIS_*
66
commands =
7-
py.test --cov-report= --cov=oicmsg tests/ -m "not network" {posargs}
7+
py.test --cov-report= --cov=oidcmsg tests/ -m "not network" {posargs}
88
codecov
99
extras = testing
1010
deps =
@@ -20,7 +20,7 @@ commands = sphinx-build -b html doc/ doc/_build/html -W
2020
ignore_errors = True
2121
extras = quality
2222
commands =
23-
isort --recursive --diff --check-only src/ tests/
23+
isort --diff --check-only src/ tests/
2424
pylama src/ tests/
2525

2626
[pep8]

0 commit comments

Comments
 (0)