Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
Trust and Identity Incubator
alternate-mdx
Commits
420ce3ab
Commit
420ce3ab
authored
Dec 14, 2021
by
Martin van Es
Browse files
Rewrite mdserver classes
parent
16f454fc
Changes
5
Hide whitespace changes
Inline
Side-by-side
mdproxy.py
View file @
420ce3ab
#!/usr/bin/env python
import
requests
import
hashlib
from
lxml
import
etree
as
ET
from
flask
import
Flask
from
flask
import
Flask
,
Response
from
urllib.parse
import
unquote
from
dateutil
import
parser
,
tz
from
datetime
import
datetime
...
...
@@ -26,6 +24,10 @@ def serve(domain, eid):
else
:
entityID
=
hasher
(
entityID
)
response
=
Response
()
response
.
headers
[
'Content-Type'
]
=
"application/samlmetadata+xml"
response
.
headers
[
'Content-Disposition'
]
=
"filename =
\"
metadata.xml
\"
"
cached
[
domain
]
=
cached
.
get
(
domain
,
{})
if
entityID
in
cached
[
domain
]:
if
cached
[
domain
][
entityID
].
valid_until
>
datetime
.
now
(
tz
.
tzutc
()):
...
...
@@ -33,15 +35,22 @@ def serve(domain, eid):
return
cached
[
domain
][
entityID
].
md
else
:
print
(
f
"request
{
entityID
}
"
)
result
=
requests
.
get
(
f
"
{
signer_url
}
/
{
domain
}
/entities/{{sha1}}
{
entityID
}
"
).
text
parsed
=
ET
.
fromstring
(
result
)
validUntil
=
parsed
.
get
(
'validUntil'
)
# cacheDuration = parsed.get('cacheDuration')
cached_entity
=
Entity
()
cached_entity
.
md
=
result
cached_entity
.
valid_until
=
parser
.
isoparse
(
validUntil
)
cached
[
domain
][
entityID
]
=
cached_entity
return
result
data
=
requests
.
get
(
f
"
{
signer_url
}
/
{
domain
}
/entities/{{sha1}}
{
entityID
}
"
).
text
try
:
parsed
=
ET
.
fromstring
(
data
)
validUntil
=
parsed
.
get
(
'validUntil'
)
# cacheDuration = parsed.get('cacheDuration')
cached_entity
=
Entity
()
cached_entity
.
md
=
data
cached_entity
.
valid_until
=
parser
.
isoparse
(
validUntil
)
cached
[
domain
][
entityID
]
=
cached_entity
except
ET
.
XMLSyntaxError
:
data
=
"No valid metadata
\n
"
response
.
headers
[
'Content-type'
]
=
"text/html"
response
.
status
=
404
response
.
data
=
data
return
response
app
.
run
(
host
=
'0.0.0.0'
,
port
=
5002
)
mdserver.py
View file @
420ce3ab
#!/usr/bin/env python
from
lxml
import
etree
as
ET
from
utils
import
read_config
,
server
,
event_notifier
from
flask
import
Flask
,
Response
from
urllib.parse
import
unquote
from
dateutil
import
tz
from
datetime
import
datetime
# import pyinotify
import
traceback
from
utils
import
read_config
,
read_domain
,
hasher
,
idps
,
\
signed
,
signer
,
Signers
,
Entity
,
event_notifier
signers
=
Signers
()
config
=
read_config
()
app
=
Flask
(
__name__
)
@
app
.
route
(
'/<domain>/entities/<path:eid>'
,
methods
=
[
'GET'
])
def
serve
(
domain
,
eid
):
entityID
=
unquote
(
eid
)
if
entityID
[:
6
]
==
"{sha1}"
:
sha1
=
entityID
[
6
:]
else
:
sha1
=
hasher
(
entityID
)
@
app
.
route
(
'/<domain>/entities/<path:entity_id>'
,
methods
=
[
'GET'
])
def
serve
(
domain
,
entity_id
):
response
=
Response
()
response
.
headers
[
'Content-Type'
]
=
"application/samlmetadata+xml"
response
.
headers
[
'Content-Disposition'
]
=
"filename =
\"
metadata.xml
\"
"
if
sha1
in
signed
[
domain
]:
signed_entity
=
signed
[
domain
][
sha1
]
if
signed_entity
.
valid_until
>
datetime
.
now
(
tz
.
tzutc
()):
response
.
data
=
signed
[
domain
][
sha1
].
md
elif
sha1
in
idps
[
domain
]:
try
:
print
(
f
"sign
{
domain
}
{
sha1
}
"
)
valid_until
=
idps
[
domain
][
sha1
].
valid_until
if
valid_until
>
datetime
.
now
(
tz
.
tzutc
()):
signed_element
=
signers
[
signer
[
domain
]](
idps
[
domain
][
sha1
].
md
)
signed_xml
=
ET
.
tostring
(
signed_element
,
pretty_print
=
True
).
decode
()
signed_entity
=
Entity
()
signed_entity
.
md
=
signed_xml
signed_entity
.
valid_until
=
idps
[
domain
][
sha1
].
valid_until
signed
[
domain
][
sha1
]
=
signed_entity
response
.
data
=
signed_xml
except
Exception
as
e
:
print
(
sha1
)
print
(
f
"
{
e
}
"
)
traceback
.
print_exc
()
else
:
try
:
response
.
data
=
server
[
domain
][
entity_id
]
except
Exception
:
response
.
data
=
"No valid metadata
\n
"
response
.
headers
[
'Content-type'
]
=
"text/html"
response
.
status
=
404
return
response
print
(
f
"serve
{
domain
}
{
sha1
}
"
)
return
response
config
=
read_config
()
for
domain
,
values
in
config
.
items
():
print
(
f
"domain:
{
domain
}
"
)
read_domain
(
domain
,
values
)
s
ign
er
[
domain
]
=
values
[
'signer'
]
conf
=
(
values
[
'metadir'
],
values
[
'signer'
]
)
s
erv
er
[
domain
]
=
conf
app
.
run
(
host
=
'127.0.0.1'
,
port
=
5001
)
event_notifier
.
start
()
app
.
run
(
host
=
'127.0.0.1'
,
port
=
5001
)
mdsigner.py
View file @
420ce3ab
...
...
@@ -14,7 +14,7 @@ idps = []
success
=
0
failed
=
0
maxthreads
=
8
signer
=
Signers
(
)[
'normal_signer'
]
signer
=
Signers
(
'normal_signer'
)
def
sign
(
xml
,
name
):
...
...
signers.py
0 → 100644
View file @
420ce3ab
from
signxml
import
XMLSigner
cert
=
open
(
"meta.crt"
).
read
()
key
=
open
(
"meta.key"
).
read
()
def
Signers
(
signer
):
def
_normal_signer
(
xml
):
print
(
"Normal signer"
)
return
XMLSigner
().
sign
(
xml
,
key
=
key
,
cert
=
cert
)
def
_test_signer
(
xml
):
print
(
"Test signer"
)
return
XMLSigner
().
sign
(
xml
,
key
=
key
,
cert
=
cert
)
def
_foobar_signer
(
xml
):
print
(
"Foobar signer"
)
return
XMLSigner
().
sign
(
xml
,
key
=
key
,
cert
=
cert
)
signers
=
{
'normal_signer'
:
_normal_signer
,
'test_signer'
:
_test_signer
,
'foobar_signer'
:
_foobar_signer
}
return
signers
[
signer
]
utils.py
100644 → 100755
View file @
420ce3ab
import
os
import
copy
import
yaml
from
lxml
import
etree
as
ET
import
hashlib
from
signxml
import
XMLSigner
from
isoduration
import
parse_duration
from
dateutil
import
parser
,
tz
from
isoduration
import
parse_duration
from
datetime
import
datetime
import
hashlib
from
urllib.parse
import
unquote
import
yaml
import
pyinotify
from
signers
import
Signers
cert
=
open
(
"meta.crt"
).
read
()
key
=
open
(
"meta.key"
).
read
()
watch_list
=
{}
watch_manager
=
pyinotify
.
WatchManager
()
watch_list
=
{}
idps
=
{}
signed
=
{}
signer
=
{}
def
read_config
():
with
open
(
'mdserver.yaml'
)
as
f
:
config
=
yaml
.
safe_load
(
f
)
return
config
def
hasher
(
entity_id
):
...
...
@@ -28,95 +26,119 @@ def hasher(entity_id):
return
sha1_digest
class
Entity
(
object
):
md
=
None
valid_until
=
0
cache_duration
=
0
class
EventProcessor
(
pyinotify
.
ProcessEvent
):
def
process_IN_CLOSE_WRITE
(
self
,
event
):
domain
=
watch_list
[
event
.
path
]
read_metadata
(
domain
,
event
.
path
)
print
(
f
"Notify
{
domain
}
{
event
.
path
}
"
)
server
[
domain
].
walk_location
(
event
.
path
)
class
Signers
(
dict
)
:
class
Entity
:
def
__init__
(
self
):
self
[
'normal_signer'
]
=
self
.
_normal_signer
self
[
'test_signer'
]
=
self
.
_test_signer
self
[
'foobar_signer'
]
=
self
.
_foobar_signer
def
_normal_signer
(
self
,
xml
):
print
(
"Normal signer"
)
return
XMLSigner
().
sign
(
xml
,
key
=
key
,
cert
=
cert
)
def
_test_signer
(
self
,
xml
):
print
(
"Test signer"
)
return
XMLSigner
().
sign
(
xml
,
key
=
key
,
cert
=
cert
)
def
_foobar_signer
(
self
,
xml
):
print
(
"Foobar signer"
)
return
XMLSigner
().
sign
(
xml
,
key
=
key
,
cert
=
cert
)
def
read_metadata
(
domain
,
mdfile
):
print
(
"--- READ METADATA --"
)
global
idps
,
signed
found
=
0
removed
=
0
old_idps
=
copy
.
deepcopy
(
idps
.
get
(
domain
,
{}))
idps
[
domain
]
=
idps
.
get
(
domain
,
{})
signed
[
domain
]
=
signed
.
get
(
domain
,
{})
tree
=
ET
.
ElementTree
(
file
=
mdfile
)
root
=
tree
.
getroot
()
ns
=
copy
.
deepcopy
(
root
.
nsmap
)
ns
[
'xml'
]
=
'http://www.w3.org/XML/1998/namespace'
validUntil
=
root
.
get
(
'validUntil'
)
cacheDuration
=
root
.
get
(
'cacheDuration'
)
valid_until
=
parser
.
isoparse
(
validUntil
)
cache_duration
=
parse_duration
(
cacheDuration
)
if
valid_until
>
datetime
.
now
(
tz
.
tzutc
()):
for
entity_descriptor
in
root
.
findall
(
'md:EntityDescriptor'
,
ns
):
entityID
=
entity_descriptor
.
attrib
.
get
(
'entityID'
,
'none'
)
self
.
md
=
None
self
.
valid_until
=
0
self
.
cache_duration
=
0
class
Resource
:
def
__init__
(
self
,
location
,
signer
):
self
.
idps
=
{}
self
.
mdfiles
=
{}
self
.
signer
=
Signers
(
signer
)
self
.
walk_location
(
location
)
def
walk_location
(
self
,
location
):
files
=
os
.
listdir
(
location
)
for
file
in
files
:
mdfile
=
os
.
path
.
realpath
(
os
.
path
.
join
(
location
,
file
))
if
os
.
path
.
isfile
(
mdfile
):
self
.
mdfiles
[
mdfile
]
=
[]
self
.
_read_metadata
(
mdfile
)
def
_read_metadata
(
self
,
mdfile
):
print
(
"--- READ METADATA --"
)
found
=
0
removed
=
0
old_idps
=
self
.
mdfiles
[
mdfile
].
copy
()
print
(
f
"old_idps:
{
old_idps
}
"
)
tree
=
ET
.
ElementTree
(
file
=
mdfile
)
root
=
tree
.
getroot
()
ns
=
root
.
nsmap
.
copy
()
ns
[
'xml'
]
=
'http://www.w3.org/XML/1998/namespace'
validUntil
=
root
.
get
(
'validUntil'
)
cacheDuration
=
root
.
get
(
'cacheDuration'
)
valid_until
=
parser
.
isoparse
(
validUntil
)
cache_duration
=
parse_duration
(
cacheDuration
)
if
valid_until
>
datetime
.
now
(
tz
.
tzutc
()):
for
entity_descriptor
in
root
.
findall
(
'md:EntityDescriptor'
,
ns
):
entityID
=
entity_descriptor
.
attrib
.
get
(
'entityID'
,
'none'
)
sha1
=
hasher
(
entityID
)
print
(
f
"{{sha1}}
{
sha1
}
{
entityID
}
"
)
entity_descriptor
.
set
(
'validUntil'
,
validUntil
)
entity_descriptor
.
set
(
'cacheDuration'
,
cacheDuration
)
entity
=
Entity
()
entity
.
md
=
entity_descriptor
entity
.
valid_until
=
valid_until
entity
.
cache_duration
=
cache_duration
self
.
idps
[
sha1
]
=
entity
self
.
__dict__
.
pop
(
sha1
,
None
)
if
sha1
in
self
.
mdfiles
[
mdfile
]:
self
.
mdfiles
[
mdfile
].
remove
(
sha1
)
found
+=
1
for
idp
in
old_idps
:
self
.
idps
.
pop
(
idp
,
None
)
self
.
__dict__
.
pop
(
idp
,
None
)
removed
+=
1
self
.
mdfiles
[
mdfile
]
=
self
.
idps
.
keys
()
print
(
f
"Found:
{
found
}
entities"
)
print
(
f
"Removed:
{
removed
}
entities"
)
print
(
f
"validUntil:
{
validUntil
}
"
)
def
__getitem__
(
self
,
key
):
entityID
=
unquote
(
key
)
if
entityID
[:
6
]
==
"{sha1}"
:
sha1
=
entityID
[
6
:]
else
:
sha1
=
hasher
(
entityID
)
print
(
f
"{{sha1}}
{
sha1
}
{
entityID
}
"
)
entity_descriptor
.
set
(
'validUntil'
,
validUntil
)
entity_descriptor
.
set
(
'cacheDuration'
,
cacheDuration
)
entity
=
Entity
()
entity
.
md
=
entity_descriptor
entity
.
valid_until
=
valid_until
entity
.
cache_duration
=
cache_duration
idps
[
domain
][
sha1
]
=
entity
signed
[
domain
].
pop
(
sha1
,
None
)
old_idps
.
pop
(
sha1
,
None
)
found
+=
1
for
idp
in
old_idps
:
idps
[
domain
].
pop
(
idp
,
None
)
signed
[
domain
].
pop
(
idp
,
None
)
removed
+=
1
print
(
f
"Found:
{
found
}
entities"
)
print
(
f
"Removed:
{
removed
}
entities"
)
print
(
f
"validUntil:
{
validUntil
}
"
)
def
read_domain
(
domain
,
values
):
metadir
=
values
[
'metadir'
]
print
(
f
" metadir:
{
metadir
}
"
)
files
=
os
.
listdir
(
metadir
)
for
file
in
files
:
mdfile
=
os
.
path
.
realpath
(
os
.
path
.
join
(
metadir
,
file
))
if
os
.
path
.
isfile
(
mdfile
):
print
(
f
" file:
{
mdfile
}
"
)
read_metadata
(
domain
,
mdfile
)
watch_list
[
mdfile
]
=
domain
watch_manager
.
add_watch
(
mdfile
,
pyinotify
.
IN_CLOSE_WRITE
)
def
read_config
():
with
open
(
'mdserver.yaml'
)
as
f
:
config
=
yaml
.
safe_load
(
f
)
return
config
if
sha1
in
self
.
__dict__
:
signed_entity
=
self
.
__dict__
[
sha1
]
if
signed_entity
.
valid_until
>
datetime
.
now
(
tz
.
tzutc
()):
data
=
self
.
__dict__
[
sha1
].
md
elif
sha1
in
self
.
idps
:
try
:
print
(
f
"sign
{
sha1
}
"
)
valid_until
=
self
.
idps
[
sha1
].
valid_until
if
valid_until
>
datetime
.
now
(
tz
.
tzutc
()):
signed_element
=
self
.
signer
(
self
.
idps
[
sha1
].
md
)
signed_xml
=
ET
.
tostring
(
signed_element
,
pretty_print
=
True
).
decode
()
signed_entity
=
Entity
()
signed_entity
.
md
=
signed_xml
signed_entity
.
valid_until
=
self
.
idps
[
sha1
].
valid_until
self
.
__dict__
[
sha1
]
=
signed_entity
data
=
signed_xml
except
Exception
as
e
:
print
(
sha1
)
print
(
f
"
{
e
}
"
)
else
:
raise
KeyError
print
(
f
"serve
{
sha1
}
"
)
return
data
class
Server
:
def
__setitem__
(
self
,
domain
,
conf
):
location
,
signer
=
conf
self
.
__dict__
[
domain
]
=
Resource
(
location
,
signer
)
watch_list
[
location
]
=
domain
watch_manager
.
add_watch
(
location
,
pyinotify
.
IN_CLOSE_WRITE
)
def
__getitem__
(
self
,
domain
):
return
self
.
__dict__
[
domain
]
server
=
Server
()
event_notifier
=
pyinotify
.
ThreadedNotifier
(
watch_manager
,
EventProcessor
())
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment