aboutsummaryrefslogtreecommitdiff
path: root/lib/classes/restapi/consumer/Base.php
blob: efb5b795af03c3e91edb93dab07287af61f5100e (plain)
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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
<?php
namespace RESTAPI\Consumer;

use AuthUserMd5;
use DBManager;
use DBManagerException;
use PDO;

/**
 * Base consumer class for the rest api
 *
 * Consumers provide means for authenticating a user and the access
 * permissions for routes are bound to specific consumers.
 *
 * @author     Jan-Hendrik Willms <tleilax+studip@gmail.com>
 * @license    GPL 2 or later
 * @since      Stud.IP 3.0
 * @deprecated Since Stud.IP 5.0. Will be removed in Stud.IP 5.2.
 */
abstract class Base extends \SimpleORMap
{
    /**
     * Each consumer type has to implement a detect feature which
     * should extract crucial information from the request and return
     * an instance of itself if the consumer detects a valid signature
     * it can respond to.
     *
     * @param mixed $request_type Type of request (optional; defaults to any)
     * @return mixed Detected consumer object or false
     */
    abstract public static function detect($request_type = null);

    /* Concrete */

    /**
     * Configures the model.
     *
     * @param array $config Configuration array
     */
    protected static function configure($config = [])
    {
        $config['db_table'] = 'api_consumers';

        parent::configure($config);
    }

    /**
     * Stores all known consumer types
     */
    protected static $known_types = [];

    /**
     * Add a consumer type to the list of consumer types
     *
     * @param String $type  Name of the type
     * @param String $class Associated consumer class
     */
    public static function addType($type, $class)
    {
        self::$known_types[$type] = $class;
    }

    /**
     * Removes a consumer type from the list of consumer types
     *
     * @param String $type Name of the type
     */
    public static function removeType($type)
    {
        unset(self::$known_types[$type]);
    }

    /**
     * Overloaded find method. Will return a concrete specialized consumer
     * object of the associated type.
     *
     * @param String $id Id of the consumer
     * @return \RESTAPI\Consumer\Base Associated consumer object (derived
     *                               from consumer base type)
     * @throws \Exception if either consumer id or consumer type is invalid
     */
    public static function find($id)
    {
        $query = "SELECT consumer_type
                  FROM api_consumers
                  WHERE consumer_id = :id";
        $statement = DBManager::get()->prepare($query);
        $statement->bindValue(':id', $id);
        $statement->execute();
        $type = $statement->fetchColumn();

        if (!isset(self::$known_types[$type])) {
            throw new \Exception('Consumer #' . $id . ' is of unknown type "' . $type . '"');
        }

        return new self::$known_types[$type]($id);
    }

    /**
     * Returns a list of all known consumers.
     *
     * @return array List of all known consumers (as specialized consumer
     *               objects)
     */
    public static function findAll()
    {
        $query = "SELECT consumer_id FROM api_consumers";
        $statement = DBManager::get()->query($query);
        $ids = $statement->fetchAll(PDO::FETCH_COLUMN);

        return array_map('self::find', $ids);
    }

    /**
     * Creates a new consumer of the given type.
     *
     * @param String $type Name of the type
     * @return \RESTAPI\Consumer\Base Consumer object of the given (derived
     *                               from consumer base type)
     * @throws \Exception if type is invalid
     */
    public static function create($type)
    {
        if (!isset(self::$known_types[$type])) {
            throw new \Exception('Consumer is of unknown type "' . $type . '"');
        }

        return new self::$known_types[$type];
    }

    /**
     * This method is used to detect a consumer (of a specific type) by
     * executing the detect method on all known consumer types.
     *
     * @param mixed $type Name of the type (optional; defaults to all types)
     * @param mixed $request_type Type of request (optional; defaults to any)
     * @return mixed Either the detected consumer or false if no consumer
     *               was detected
     * @throws \Exception if type is invalid
     */
    public static function detectConsumer($type = null, $request_type = null)
    {
        $needles = $type === null
                 ? array_keys(self::$known_types)
                 : [$type];
        foreach ($needles as $needle) {
            if (!isset(self::$known_types)) {
                throw new \Exception('Trying to detect consumer of unkown type "' . $needle . '"');
            }
            $consumer_class = self::$known_types[$needle];
            if ($consumer = $consumer_class::detect($request_type)) {
                return $consumer;
            }
        }
        return false;
    }

    /**
     * Contains user information
     */
    protected $user = null;

    /**
     * Extended SimpleORMap constructor. A certain user can be injected upon
     * creation.
     *
     * @param mixed $id Id of the consumer or null to create a new one
     * @param mixed $user Either a user object or id to inject to the consumer
     *                    or null if no user should be injected
     */
    public function __construct($id = null, $user = null)
    {
        parent::__construct($id);

        if ($user !== null) {
            $this->setUser($user);
        }
    }

    /**
     * Retrieve the api permissions associated with this consumer.
     *
     * @return \RESTAPI\ConsumerPermissions Permission object for this consumer
     */
    public function getPermissions()
    {
        return \RESTAPI\ConsumerPermissions::get($this->id);
    }

    /**
     * Inject a user to this consumer. Injecting in this context refers to
     * "having a user authenticated by this consumer".
     *
     * @param mixed $user Either a user object or a user id
     * @return \RESTAPI\Consumer\Base Returns instance of self to allow
     *                               chaining
     */
    public function setUser($user)
    {
        if (!is_object($user)) {
            $user = \User::findFull($user);
        }
        $this->user = $user;
        return $this;
    }

    /**
     * Returns whether the consumer has an injected user or not.
     *
     * @return bool True if a valid user is found, false otherwise
     */
    public function hasUser()
    {
        return $this->user !== null && $this->user->id && $this->user->id !== 'nobody';
    }

    /**
     * Return the injected user.
     *
     * @param mixed User object or false if no user was injected
     */
    public function getUser()
    {
        return $this->user;
    }
}