File: consents.py

package info (click to toggle)
python-globus-sdk 4.3.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 5,144 kB
  • sloc: python: 35,242; sh: 37; makefile: 35
file content (105 lines) | stat: -rw-r--r-- 3,361 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
from __future__ import annotations

import uuid
from collections import defaultdict, namedtuple
from dataclasses import dataclass, field
from datetime import datetime, timedelta

from globus_sdk.scopes import Scope, ScopeParser
from globus_sdk.scopes.consents import Consent, ConsentForest

ScopeRepr = namedtuple("Scope", ["id", "name"])


@dataclass
class ConsentTest(Consent):
    """
    A convenience Consent data subclass with default values for most fields to make
      test case definition less verbose.

    Required fields: client, scope, scope_name
    """

    client: uuid.UUID | str
    scope: uuid.UUID | str
    scope_name: str
    id: int = field(default_factory=lambda: uuid.uuid1().int)
    effective_identity: uuid.UUID | str = str(uuid.uuid4())
    dependency_path: list[int] = field(default_factory=list)
    created: datetime = field(
        default_factory=lambda: datetime.now() - timedelta(days=1)
    )
    updated: datetime = field(
        default_factory=lambda: datetime.now() - timedelta(days=1)
    )
    last_used: datetime = field(default_factory=datetime.now)
    status: str = "approved"
    allows_refresh: bool = True
    auto_approved: bool = False
    atomically_revocable: bool = False

    def __post_init__(self):
        # Append self to the dependency path if it's not already there
        if not self.dependency_path or self.dependency_path[-1] != self.id:
            self.dependency_path.append(self.id)

    @classmethod
    def of(
        cls,
        client: str,
        scope: ScopeRepr,
        *,
        parent: ConsentTest | None = None,
        **kwargs,
    ) -> ConsentTest:
        return cls(
            client=client,
            scope=scope.id,
            scope_name=scope.name,
            dependency_path=list(parent.dependency_path) if parent else [],
            **kwargs,
        )


def make_consent_forest(scopes: list[str | Scope] | str | Scope) -> ConsentForest:
    """
    Creates a consent forest from a list of scope strings or scope objects.
    Client and Scope IDs are generated at random.
    """
    _scopes = _normalize_scopes(scopes)
    scope_id_mapping: dict[str, str] = defaultdict(lambda: str(uuid.uuid4()))
    consents = []
    for scope in _scopes:
        consents.extend(_generate_consents(scope, scope_id_mapping))
    return ConsentForest(consents)


def _normalize_scopes(scopes: list[str | Scope] | str | Scope) -> list[Scope]:
    if isinstance(scopes, Scope):
        return [scopes]
    elif isinstance(scopes, str):
        return ScopeParser.parse(scopes)
    else:
        to_return = []
        for scope in scopes:
            to_return.extend(_normalize_scopes(scope))
        return to_return


def _generate_consents(
    scope: Scope, scope_id_mapping: dict[str, str], parent: ConsentTest | None = None
) -> list[Consent]:
    """Generates a list of consents for a scope and its children."""
    consents = []
    client_id = str(uuid.uuid4())
    scope_string = scope.scope_string
    scope_id = scope_id_mapping[scope_string]
    consent = ConsentTest.of(
        client_id, ScopeRepr(scope_id, scope_string), parent=parent
    )
    consents.append(consent)
    for dependent_scope in scope.dependencies:
        consents.extend(
            _generate_consents(dependent_scope, scope_id_mapping, parent=consent)
        )
    return consents