Passed
Push — master ( c3ec55...6fe0c7 )
by Simon
04:07
created

RoleConfigurationBase::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 2
dl 0
loc 4
rs 10
c 1
b 0
f 0
cc 1
nc 1
nop 2
1
<?php
2
/******************************************************************************
3
 * Wikipedia Account Creation Assistance tool                                 *
4
 *                                                                            *
5
 * All code in this file is released into the public domain by the ACC        *
6
 * Development Team. Please see team.json for a list of contributors.         *
7
 ******************************************************************************/
8
9
namespace Waca\Security;
10
11
abstract class RoleConfigurationBase
12
{
13
    const ACCESS_ALLOW = 1;
14
    const ACCESS_DENY = -1;
15
    const ACCESS_DEFAULT = 0;
16
    const MAIN = 'main';
17
    const ALL = '*';
18
19
    protected array $roleConfig;
20
    protected array $identificationExempt;
21
22
    protected function __construct(array $roleConfig, array $identificationExempt)
23
    {
24
        $this->roleConfig = $roleConfig;
25
        $this->identificationExempt = $identificationExempt;
26
    }
27
28
    /**
29
     * Takes an array of role names and flattens the values to a single
30
     * resultant role configuration.
31
     *
32
     * @param string[] $activeRoles
33
     * @category Security-Critical
34
     */
35
    public function getResultantRole(array $activeRoles): array
36
    {
37
        $result = array();
38
39
        $roleConfig = $this->getApplicableRoles($activeRoles);
40
41
        // Iterate over every page in every role
42
        foreach ($roleConfig as $role) {
43
            foreach ($role as $page => $pageRights) {
44
                // Create holder in result for this page
45
                if (!isset($result[$page])) {
46
                    $result[$page] = array();
47
                }
48
49
                foreach ($pageRights as $action => $permission) {
50
                    // Deny takes precedence, so if it's set, don't change it.
51
                    if (isset($result[$page][$action])) {
52
                        if ($result[$page][$action] === RoleConfigurationBase::ACCESS_DENY) {
53
                            continue;
54
                        }
55
                    }
56
57
                    if ($permission === RoleConfigurationBase::ACCESS_DEFAULT) {
58
                        // Configured to do precisely nothing.
59
                        continue;
60
                    }
61
62
                    $result[$page][$action] = $permission;
63
                }
64
            }
65
        }
66
67
        return $result;
68
    }
69
70
    /**
71
     * Returns a set of all roles which are available to be set.
72
     *
73
     * Hidden roles and implicit roles are excluded.
74
     */
75
    public function getAvailableRoles(): array
76
    {
77
        // remove the implicit roles
78
        $possible = array_diff(array_keys($this->roleConfig), array('public', 'loggedIn', 'user'));
79
80
        $actual = array();
81
82
        foreach ($possible as $role) {
83
            if (!isset($this->roleConfig[$role]['_hidden'])) {
84
                $actual[$role] = array(
85
                    'description' => $this->roleConfig[$role]['_description'],
86
                    'editableBy'  => $this->roleConfig[$role]['_editableBy'],
87
                    'globalOnly'  => isset($this->roleConfig[$role]['_globalOnly']) && $this->roleConfig[$role]['_globalOnly'],
88
                );
89
            }
90
        }
91
92
        return $actual;
93
    }
94
95
    /**
96
     * Returns a boolean for whether the provided role requires identification
97
     * before being used by a user.
98
     *
99
     * @category Security-Critical
100
     */
101
    public function roleNeedsIdentification(string $role): bool
102
    {
103
        if (in_array($role, $this->identificationExempt)) {
104
            return false;
105
        }
106
107
        return true;
108
    }
109
110
    /**
111
     * Takes an array of role names, and returns all the relevant roles for that
112
     * set, including any child roles found recursively.
113
     *
114
     * @param array $roles The names of roles to start searching with
115
     */
116
    private function getApplicableRoles(array $roles): array
117
    {
118
        $available = array();
119
120
        foreach ($roles as $role) {
121
            if (!isset($this->roleConfig[$role])) {
122
                // wat
123
                continue;
124
            }
125
126
            $available[$role] = $this->roleConfig[$role];
127
128
            if (isset($available[$role]['_childRoles'])) {
129
                $childRoles = $this->getApplicableRoles($available[$role]['_childRoles']);
130
                $available = array_merge($available, $childRoles);
131
132
                unset($available[$role]['_childRoles']);
133
            }
134
135
            foreach (array('_hidden', '_editableBy', '_description') as $item) {
136
                if (isset($available[$role][$item])) {
137
                    unset($available[$role][$item]);
138
                }
139
            }
140
        }
141
142
        return $available;
143
    }
144
}