Skip to content

Commit

Permalink
Merge pull request #113 from c00kiemon5ter/feature-microservice-attri…
Browse files Browse the repository at this point in the history
…bute-processor

Add attribute processor microservice
  • Loading branch information
johanlundberg authored Jul 11, 2017
2 parents 85a1090 + 99993b1 commit 3c4e6e6
Show file tree
Hide file tree
Showing 7 changed files with 169 additions and 0 deletions.
17 changes: 17 additions & 0 deletions example/plugins/microservices/attribute_processor.yaml.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
module: satosa.micro_services.attribute_processor.AttributeProcessor
name: AttributeProcessor
config:
process:
- attribute: gender
processors:
- name: GenderToSchacProcessor
module: satosa.micro_services.processors.gender_processor
- attribute: identifier
processors:
- name: HashProcessor
module: satosa.micro_services.processors.hash_processor
hash_alg: sha256
salt: abcdef0123456789
- name: ScopeProcessor
module: satosa.micro_services.processors.scope_processor
scope: example.com
73 changes: 73 additions & 0 deletions src/satosa/micro_services/attribute_processor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import importlib
import json
import logging

from satosa.exception import SATOSAError
from satosa.logging_util import satosa_logging
from satosa.micro_services.base import ResponseMicroService


logger = logging.getLogger(__name__)

CONFIG_KEY_ROOT = 'process'
CONFIG_KEY_MODULE = 'module'
CONFIG_KEY_CLASSNAME = 'name'
CONFIG_KEY_ATTRIBUTE = 'attribute'
CONFIG_KEY_PROCESSORS = 'processors'


class AttributeProcessor(ResponseMicroService):
"""
This microservice enables users to define modules that process internal
attributes and their values.
Example configuration:
# file: attribute_processor.yaml
module: satosa.micro_services.attribute_processor.AttributeProcessor
process:
- attribute: gender
- name: GenderToSchacProcessor
module: satosa.micro_services.processors.gender_processor
- attribute: identifier
processors:
- name: HashProcessor
module: satosa.micro_services.processors.hash_processor
hash_alg: sha256
salt: abcdef0123456789
- name: ScopeProcessor
module: satosa.micro_services.processors.scope_processor
scope: example
"""
def __init__(self, config, *args, **kwargs):
super().__init__(*args, **kwargs)
self.config = config
self.processes = config[CONFIG_KEY_ROOT]

def process(self, context, data):
for process in self.processes:
attribute = process[CONFIG_KEY_ATTRIBUTE]
processors = process[CONFIG_KEY_PROCESSORS]
for processor in processors:
module = importlib.import_module(processor[CONFIG_KEY_MODULE])
module_cls = getattr(module, processor[CONFIG_KEY_CLASSNAME])
instance = module_cls()

kwargs = processor.copy()
kwargs.pop(CONFIG_KEY_MODULE)
kwargs.pop(CONFIG_KEY_CLASSNAME)

try:
instance.process(data, attribute, **kwargs)
except AttributeProcessorWarning as w:
satosa_logging(logger, logging.WARNING, w, context.state)

return super().process(context, data)


class AttributeProcessorWarning(SATOSAError):
pass


class AttributeProcessorError(SATOSAError):
pass
Empty file.
6 changes: 6 additions & 0 deletions src/satosa/micro_services/processors/base_processor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
class BaseProcessor(object):
def __init__(self):
pass

def process(internal_data, attribute, **kwargs):
pass
25 changes: 25 additions & 0 deletions src/satosa/micro_services/processors/gender_processor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from .base_processor import BaseProcessor

from enum import Enum, unique


@unique
class Gender(Enum):
NOT_KNOWN = 0
MALE = 1
FEMALE = 2
NOT_SPECIFIED = 9


class GenderToSchacProcessor(BaseProcessor):
def process(self, internal_data, attribute, **kwargs):
attributes = internal_data.attributes
value = attributes.get(attribute, [None])[0]

if value:
representation = getattr(
Gender, value.upper().replace(' ', '_'), Gender.NOT_KNOWN)
else:
representation = Gender.NOT_SPECIFIED

attributes[attribute][0] = str(representation.value)
31 changes: 31 additions & 0 deletions src/satosa/micro_services/processors/hash_processor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from ..attribute_processor import AttributeProcessorError
from .base_processor import BaseProcessor

import hashlib


CONFIG_KEY_SALT = 'salt'
CONFIG_DEFAULT_SALT = ''
CONFIG_KEY_HASHALGO = 'hash_algo'
CONFIG_DEFAULT_HASHALGO = 'sha256'


class HashProcessor(BaseProcessor):
def process(self, internal_data, attribute, **kwargs):
salt = kwargs.get(CONFIG_KEY_HASHALGO, CONFIG_DEFAULT_SALT)
hash_algo = kwargs.get(CONFIG_KEY_HASHALGO, CONFIG_DEFAULT_HASHALGO)
if hash_algo not in hashlib.algorithms_available:
raise AttributeProcessorError(
"Hash algorithm not supported: {}".format(hash_algo))

attributes = internal_data.attributes
value = attributes.get(attribute, [None])[0]
if value is None:
raise AttributeProcessorError(
"No value for attribute: {}".format(attribute))

hasher = hashlib.new(hash_algo)
hasher.update(value.encode('utf-8'))
hasher.update(salt.encode('utf-8'))
value_hashed = hasher.hexdigest()
attributes[attribute][0] = value_hashed
17 changes: 17 additions & 0 deletions src/satosa/micro_services/processors/scope_processor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from ..attribute_processor import AttributeProcessorError
from .base_processor import BaseProcessor


CONFIG_KEY_SCOPE = 'scope'
CONFIG_DEFAULT_SCOPE = ''


class ScopeProcessor(BaseProcessor):
def process(self, internal_data, attribute, **kwargs):
scope = kwargs.get(CONFIG_KEY_SCOPE, CONFIG_DEFAULT_SCOPE)
if scope is None or scope == '':
raise AttributeProcessorError("No scope set.")

attributes = internal_data.attributes
value = attributes.get(attribute, [None])[0]
attributes[attribute][0] = value + '@' + scope

0 comments on commit 3c4e6e6

Please sign in to comment.