Skip to content

Commit 3c4e6e6

Browse files
Merge pull request #113 from c00kiemon5ter/feature-microservice-attribute-processor
Add attribute processor microservice
2 parents 85a1090 + 99993b1 commit 3c4e6e6

File tree

7 files changed

+169
-0
lines changed

7 files changed

+169
-0
lines changed
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
module: satosa.micro_services.attribute_processor.AttributeProcessor
2+
name: AttributeProcessor
3+
config:
4+
process:
5+
- attribute: gender
6+
processors:
7+
- name: GenderToSchacProcessor
8+
module: satosa.micro_services.processors.gender_processor
9+
- attribute: identifier
10+
processors:
11+
- name: HashProcessor
12+
module: satosa.micro_services.processors.hash_processor
13+
hash_alg: sha256
14+
salt: abcdef0123456789
15+
- name: ScopeProcessor
16+
module: satosa.micro_services.processors.scope_processor
17+
scope: example.com
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import importlib
2+
import json
3+
import logging
4+
5+
from satosa.exception import SATOSAError
6+
from satosa.logging_util import satosa_logging
7+
from satosa.micro_services.base import ResponseMicroService
8+
9+
10+
logger = logging.getLogger(__name__)
11+
12+
CONFIG_KEY_ROOT = 'process'
13+
CONFIG_KEY_MODULE = 'module'
14+
CONFIG_KEY_CLASSNAME = 'name'
15+
CONFIG_KEY_ATTRIBUTE = 'attribute'
16+
CONFIG_KEY_PROCESSORS = 'processors'
17+
18+
19+
class AttributeProcessor(ResponseMicroService):
20+
"""
21+
This microservice enables users to define modules that process internal
22+
attributes and their values.
23+
24+
Example configuration:
25+
26+
# file: attribute_processor.yaml
27+
module: satosa.micro_services.attribute_processor.AttributeProcessor
28+
process:
29+
- attribute: gender
30+
- name: GenderToSchacProcessor
31+
module: satosa.micro_services.processors.gender_processor
32+
- attribute: identifier
33+
processors:
34+
- name: HashProcessor
35+
module: satosa.micro_services.processors.hash_processor
36+
hash_alg: sha256
37+
salt: abcdef0123456789
38+
- name: ScopeProcessor
39+
module: satosa.micro_services.processors.scope_processor
40+
scope: example
41+
"""
42+
def __init__(self, config, *args, **kwargs):
43+
super().__init__(*args, **kwargs)
44+
self.config = config
45+
self.processes = config[CONFIG_KEY_ROOT]
46+
47+
def process(self, context, data):
48+
for process in self.processes:
49+
attribute = process[CONFIG_KEY_ATTRIBUTE]
50+
processors = process[CONFIG_KEY_PROCESSORS]
51+
for processor in processors:
52+
module = importlib.import_module(processor[CONFIG_KEY_MODULE])
53+
module_cls = getattr(module, processor[CONFIG_KEY_CLASSNAME])
54+
instance = module_cls()
55+
56+
kwargs = processor.copy()
57+
kwargs.pop(CONFIG_KEY_MODULE)
58+
kwargs.pop(CONFIG_KEY_CLASSNAME)
59+
60+
try:
61+
instance.process(data, attribute, **kwargs)
62+
except AttributeProcessorWarning as w:
63+
satosa_logging(logger, logging.WARNING, w, context.state)
64+
65+
return super().process(context, data)
66+
67+
68+
class AttributeProcessorWarning(SATOSAError):
69+
pass
70+
71+
72+
class AttributeProcessorError(SATOSAError):
73+
pass

src/satosa/micro_services/processors/__init__.py

Whitespace-only changes.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
class BaseProcessor(object):
2+
def __init__(self):
3+
pass
4+
5+
def process(internal_data, attribute, **kwargs):
6+
pass
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
from .base_processor import BaseProcessor
2+
3+
from enum import Enum, unique
4+
5+
6+
@unique
7+
class Gender(Enum):
8+
NOT_KNOWN = 0
9+
MALE = 1
10+
FEMALE = 2
11+
NOT_SPECIFIED = 9
12+
13+
14+
class GenderToSchacProcessor(BaseProcessor):
15+
def process(self, internal_data, attribute, **kwargs):
16+
attributes = internal_data.attributes
17+
value = attributes.get(attribute, [None])[0]
18+
19+
if value:
20+
representation = getattr(
21+
Gender, value.upper().replace(' ', '_'), Gender.NOT_KNOWN)
22+
else:
23+
representation = Gender.NOT_SPECIFIED
24+
25+
attributes[attribute][0] = str(representation.value)
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
from ..attribute_processor import AttributeProcessorError
2+
from .base_processor import BaseProcessor
3+
4+
import hashlib
5+
6+
7+
CONFIG_KEY_SALT = 'salt'
8+
CONFIG_DEFAULT_SALT = ''
9+
CONFIG_KEY_HASHALGO = 'hash_algo'
10+
CONFIG_DEFAULT_HASHALGO = 'sha256'
11+
12+
13+
class HashProcessor(BaseProcessor):
14+
def process(self, internal_data, attribute, **kwargs):
15+
salt = kwargs.get(CONFIG_KEY_HASHALGO, CONFIG_DEFAULT_SALT)
16+
hash_algo = kwargs.get(CONFIG_KEY_HASHALGO, CONFIG_DEFAULT_HASHALGO)
17+
if hash_algo not in hashlib.algorithms_available:
18+
raise AttributeProcessorError(
19+
"Hash algorithm not supported: {}".format(hash_algo))
20+
21+
attributes = internal_data.attributes
22+
value = attributes.get(attribute, [None])[0]
23+
if value is None:
24+
raise AttributeProcessorError(
25+
"No value for attribute: {}".format(attribute))
26+
27+
hasher = hashlib.new(hash_algo)
28+
hasher.update(value.encode('utf-8'))
29+
hasher.update(salt.encode('utf-8'))
30+
value_hashed = hasher.hexdigest()
31+
attributes[attribute][0] = value_hashed
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
from ..attribute_processor import AttributeProcessorError
2+
from .base_processor import BaseProcessor
3+
4+
5+
CONFIG_KEY_SCOPE = 'scope'
6+
CONFIG_DEFAULT_SCOPE = ''
7+
8+
9+
class ScopeProcessor(BaseProcessor):
10+
def process(self, internal_data, attribute, **kwargs):
11+
scope = kwargs.get(CONFIG_KEY_SCOPE, CONFIG_DEFAULT_SCOPE)
12+
if scope is None or scope == '':
13+
raise AttributeProcessorError("No scope set.")
14+
15+
attributes = internal_data.attributes
16+
value = attributes.get(attribute, [None])[0]
17+
attributes[attribute][0] = value + '@' + scope

0 commit comments

Comments
 (0)