Passed
Push — master ( 6c9b99...b6364c )
by Morris
11:55 queued 11s
created
apps/twofactor_backupcodes/lib/Db/BackupCodeMapper.php 1 patch
Indentation   +31 added lines, -31 removed lines patch added patch discarded remove patch
@@ -34,41 +34,41 @@
 block discarded – undo
34 34
  * @template-extends QBMapper<BackupCode>
35 35
  */
36 36
 class BackupCodeMapper extends QBMapper {
37
-	public function __construct(IDBConnection $db) {
38
-		parent::__construct($db, 'twofactor_backupcodes');
39
-	}
37
+    public function __construct(IDBConnection $db) {
38
+        parent::__construct($db, 'twofactor_backupcodes');
39
+    }
40 40
 
41
-	/**
42
-	 * @param IUser $user
43
-	 * @return BackupCode[]
44
-	 */
45
-	public function getBackupCodes(IUser $user) {
46
-		/* @var IQueryBuilder $qb */
47
-		$qb = $this->db->getQueryBuilder();
41
+    /**
42
+     * @param IUser $user
43
+     * @return BackupCode[]
44
+     */
45
+    public function getBackupCodes(IUser $user) {
46
+        /* @var IQueryBuilder $qb */
47
+        $qb = $this->db->getQueryBuilder();
48 48
 
49
-		$qb->select('id', 'user_id', 'code', 'used')
50
-			->from('twofactor_backupcodes')
51
-			->where($qb->expr()->eq('user_id', $qb->createNamedParameter($user->getUID())));
49
+        $qb->select('id', 'user_id', 'code', 'used')
50
+            ->from('twofactor_backupcodes')
51
+            ->where($qb->expr()->eq('user_id', $qb->createNamedParameter($user->getUID())));
52 52
 
53
-		return self::findEntities($qb);
54
-	}
53
+        return self::findEntities($qb);
54
+    }
55 55
 
56
-	/**
57
-	 * @param IUser $user
58
-	 */
59
-	public function deleteCodes(IUser $user) {
60
-		$this->deleteCodesByUserId($user->getUID());
61
-	}
56
+    /**
57
+     * @param IUser $user
58
+     */
59
+    public function deleteCodes(IUser $user) {
60
+        $this->deleteCodesByUserId($user->getUID());
61
+    }
62 62
 
63
-	/**
64
-	 * @param string $uid
65
-	 */
66
-	public function deleteCodesByUserId($uid) {
67
-		/* @var IQueryBuilder $qb */
68
-		$qb = $this->db->getQueryBuilder();
63
+    /**
64
+     * @param string $uid
65
+     */
66
+    public function deleteCodesByUserId($uid) {
67
+        /* @var IQueryBuilder $qb */
68
+        $qb = $this->db->getQueryBuilder();
69 69
 
70
-		$qb->delete('twofactor_backupcodes')
71
-			->where($qb->expr()->eq('user_id', $qb->createNamedParameter($uid)));
72
-		$qb->execute();
73
-	}
70
+        $qb->delete('twofactor_backupcodes')
71
+            ->where($qb->expr()->eq('user_id', $qb->createNamedParameter($uid)));
72
+        $qb->execute();
73
+    }
74 74
 }
Please login to merge, or discard this patch.
apps/contactsinteraction/lib/Db/RecentContactMapper.php 1 patch
Indentation   +106 added lines, -106 removed lines patch added patch discarded remove patch
@@ -34,110 +34,110 @@
 block discarded – undo
34 34
  * @template-extends QBMapper<RecentContact>
35 35
  */
36 36
 class RecentContactMapper extends QBMapper {
37
-	public const TABLE_NAME = 'recent_contact';
38
-
39
-	public function __construct(IDBConnection $db) {
40
-		parent::__construct($db, self::TABLE_NAME);
41
-	}
42
-
43
-	/**
44
-	 * @return RecentContact[]
45
-	 */
46
-	public function findAll(string $uid): array {
47
-		$qb = $this->db->getQueryBuilder();
48
-
49
-		$select = $qb
50
-			->select('*')
51
-			->from($this->getTableName())
52
-			->where($qb->expr()->eq('actor_uid', $qb->createNamedParameter($uid)));
53
-
54
-		return $this->findEntities($select);
55
-	}
56
-
57
-	/**
58
-	 * @param string $uid
59
-	 * @param int $id
60
-	 *
61
-	 * @return RecentContact
62
-	 * @throws DoesNotExistException
63
-	 */
64
-	public function find(string $uid, int $id): RecentContact {
65
-		$qb = $this->db->getQueryBuilder();
66
-
67
-		$select = $qb
68
-			->select('*')
69
-			->from($this->getTableName())
70
-			->where($qb->expr()->eq('id', $qb->createNamedParameter($id, $qb::PARAM_INT)))
71
-			->andWhere($qb->expr()->eq('actor_uid', $qb->createNamedParameter($uid)));
72
-
73
-		return $this->findEntity($select);
74
-	}
75
-
76
-	/**
77
-	 * @param IUser $user
78
-	 * @param string|null $uid
79
-	 * @param string|null $email
80
-	 * @param string|null $cloudId
81
-	 *
82
-	 * @return RecentContact[]
83
-	 */
84
-	public function findMatch(IUser $user,
85
-							  ?string $uid,
86
-							  ?string $email,
87
-							  ?string $cloudId): array {
88
-		$qb = $this->db->getQueryBuilder();
89
-
90
-		$or = $qb->expr()->orX();
91
-		if ($uid !== null) {
92
-			$or->add($qb->expr()->eq('uid', $qb->createNamedParameter($uid)));
93
-		}
94
-		if ($email !== null) {
95
-			$or->add($qb->expr()->eq('email', $qb->createNamedParameter($email)));
96
-		}
97
-		if ($cloudId !== null) {
98
-			$or->add($qb->expr()->eq('federated_cloud_id', $qb->createNamedParameter($cloudId)));
99
-		}
100
-
101
-		$select = $qb
102
-			->select('*')
103
-			->from($this->getTableName())
104
-			->where($or)
105
-			->andWhere($qb->expr()->eq('actor_uid', $qb->createNamedParameter($user->getUID())));
106
-
107
-		return $this->findEntities($select);
108
-	}
109
-
110
-	/**
111
-	 * @param string $uid
112
-	 * @return int|null
113
-	 */
114
-	public function findLastUpdatedForUserId(string $uid):?int {
115
-		$qb = $this->db->getQueryBuilder();
116
-
117
-		$select = $qb
118
-			->select('last_contact')
119
-			->from($this->getTableName())
120
-			->where($qb->expr()->eq('actor_uid', $qb->createNamedParameter($uid)))
121
-			->orderBy('last_contact', 'DESC')
122
-			->setMaxResults(1);
123
-
124
-		$cursor = $select->execute();
125
-		$row = $cursor->fetch();
126
-
127
-		if ($row === false) {
128
-			return null;
129
-		}
130
-
131
-		return (int)$row['last_contact'];
132
-	}
133
-
134
-	public function cleanUp(int $olderThan): void {
135
-		$qb = $this->db->getQueryBuilder();
136
-
137
-		$delete = $qb
138
-			->delete($this->getTableName())
139
-			->where($qb->expr()->lt('last_contact', $qb->createNamedParameter($olderThan)));
140
-
141
-		$delete->execute();
142
-	}
37
+    public const TABLE_NAME = 'recent_contact';
38
+
39
+    public function __construct(IDBConnection $db) {
40
+        parent::__construct($db, self::TABLE_NAME);
41
+    }
42
+
43
+    /**
44
+     * @return RecentContact[]
45
+     */
46
+    public function findAll(string $uid): array {
47
+        $qb = $this->db->getQueryBuilder();
48
+
49
+        $select = $qb
50
+            ->select('*')
51
+            ->from($this->getTableName())
52
+            ->where($qb->expr()->eq('actor_uid', $qb->createNamedParameter($uid)));
53
+
54
+        return $this->findEntities($select);
55
+    }
56
+
57
+    /**
58
+     * @param string $uid
59
+     * @param int $id
60
+     *
61
+     * @return RecentContact
62
+     * @throws DoesNotExistException
63
+     */
64
+    public function find(string $uid, int $id): RecentContact {
65
+        $qb = $this->db->getQueryBuilder();
66
+
67
+        $select = $qb
68
+            ->select('*')
69
+            ->from($this->getTableName())
70
+            ->where($qb->expr()->eq('id', $qb->createNamedParameter($id, $qb::PARAM_INT)))
71
+            ->andWhere($qb->expr()->eq('actor_uid', $qb->createNamedParameter($uid)));
72
+
73
+        return $this->findEntity($select);
74
+    }
75
+
76
+    /**
77
+     * @param IUser $user
78
+     * @param string|null $uid
79
+     * @param string|null $email
80
+     * @param string|null $cloudId
81
+     *
82
+     * @return RecentContact[]
83
+     */
84
+    public function findMatch(IUser $user,
85
+                              ?string $uid,
86
+                              ?string $email,
87
+                              ?string $cloudId): array {
88
+        $qb = $this->db->getQueryBuilder();
89
+
90
+        $or = $qb->expr()->orX();
91
+        if ($uid !== null) {
92
+            $or->add($qb->expr()->eq('uid', $qb->createNamedParameter($uid)));
93
+        }
94
+        if ($email !== null) {
95
+            $or->add($qb->expr()->eq('email', $qb->createNamedParameter($email)));
96
+        }
97
+        if ($cloudId !== null) {
98
+            $or->add($qb->expr()->eq('federated_cloud_id', $qb->createNamedParameter($cloudId)));
99
+        }
100
+
101
+        $select = $qb
102
+            ->select('*')
103
+            ->from($this->getTableName())
104
+            ->where($or)
105
+            ->andWhere($qb->expr()->eq('actor_uid', $qb->createNamedParameter($user->getUID())));
106
+
107
+        return $this->findEntities($select);
108
+    }
109
+
110
+    /**
111
+     * @param string $uid
112
+     * @return int|null
113
+     */
114
+    public function findLastUpdatedForUserId(string $uid):?int {
115
+        $qb = $this->db->getQueryBuilder();
116
+
117
+        $select = $qb
118
+            ->select('last_contact')
119
+            ->from($this->getTableName())
120
+            ->where($qb->expr()->eq('actor_uid', $qb->createNamedParameter($uid)))
121
+            ->orderBy('last_contact', 'DESC')
122
+            ->setMaxResults(1);
123
+
124
+        $cursor = $select->execute();
125
+        $row = $cursor->fetch();
126
+
127
+        if ($row === false) {
128
+            return null;
129
+        }
130
+
131
+        return (int)$row['last_contact'];
132
+    }
133
+
134
+    public function cleanUp(int $olderThan): void {
135
+        $qb = $this->db->getQueryBuilder();
136
+
137
+        $delete = $qb
138
+            ->delete($this->getTableName())
139
+            ->where($qb->expr()->lt('last_contact', $qb->createNamedParameter($olderThan)));
140
+
141
+        $delete->execute();
142
+    }
143 143
 }
Please login to merge, or discard this patch.
lib/public/AppFramework/Db/QBMapper.php 1 patch
Indentation   +301 added lines, -301 removed lines patch added patch discarded remove patch
@@ -44,305 +44,305 @@
 block discarded – undo
44 44
  */
45 45
 abstract class QBMapper {
46 46
 
47
-	/** @var string */
48
-	protected $tableName;
49
-
50
-	/** @var string|class-string<T> */
51
-	protected $entityClass;
52
-
53
-	/** @var IDBConnection */
54
-	protected $db;
55
-
56
-	/**
57
-	 * @param IDBConnection $db Instance of the Db abstraction layer
58
-	 * @param string $tableName the name of the table. set this to allow entity
59
-	 * @param string|null $entityClass the name of the entity that the sql should be
60
-	 * @psalm-param class-string<T>|null $entityClass the name of the entity that the sql should be
61
-	 * mapped to queries without using sql
62
-	 * @since 14.0.0
63
-	 */
64
-	public function __construct(IDBConnection $db, string $tableName, string $entityClass = null) {
65
-		$this->db = $db;
66
-		$this->tableName = $tableName;
67
-
68
-		// if not given set the entity name to the class without the mapper part
69
-		// cache it here for later use since reflection is slow
70
-		if ($entityClass === null) {
71
-			$this->entityClass = str_replace('Mapper', '', \get_class($this));
72
-		} else {
73
-			$this->entityClass = $entityClass;
74
-		}
75
-	}
76
-
77
-
78
-	/**
79
-	 * @return string the table name
80
-	 * @since 14.0.0
81
-	 */
82
-	public function getTableName(): string {
83
-		return $this->tableName;
84
-	}
85
-
86
-
87
-	/**
88
-	 * Deletes an entity from the table
89
-	 * @param Entity $entity the entity that should be deleted
90
-	 * @psalm-param T $entity the entity that should be deleted
91
-	 * @return Entity the deleted entity
92
-	 * @psalm-return T the deleted entity
93
-	 * @since 14.0.0
94
-	 */
95
-	public function delete(Entity $entity): Entity {
96
-		$qb = $this->db->getQueryBuilder();
97
-
98
-		$idType = $this->getParameterTypeForProperty($entity, 'id');
99
-
100
-		$qb->delete($this->tableName)
101
-			->where(
102
-				$qb->expr()->eq('id', $qb->createNamedParameter($entity->getId(), $idType))
103
-			);
104
-		$qb->execute();
105
-		return $entity;
106
-	}
107
-
108
-
109
-	/**
110
-	 * Creates a new entry in the db from an entity
111
-	 * @param Entity $entity the entity that should be created
112
-	 * @psalm-param T $entity the entity that should be created
113
-	 * @return Entity the saved entity with the set id
114
-	 * @psalm-return T the saved entity with the set id
115
-	 * @since 14.0.0
116
-	 */
117
-	public function insert(Entity $entity): Entity {
118
-		// get updated fields to save, fields have to be set using a setter to
119
-		// be saved
120
-		$properties = $entity->getUpdatedFields();
121
-
122
-		$qb = $this->db->getQueryBuilder();
123
-		$qb->insert($this->tableName);
124
-
125
-		// build the fields
126
-		foreach ($properties as $property => $updated) {
127
-			$column = $entity->propertyToColumn($property);
128
-			$getter = 'get' . ucfirst($property);
129
-			$value = $entity->$getter();
130
-
131
-			$type = $this->getParameterTypeForProperty($entity, $property);
132
-			$qb->setValue($column, $qb->createNamedParameter($value, $type));
133
-		}
134
-
135
-		$qb->execute();
136
-
137
-		if ($entity->id === null) {
138
-			// When autoincrement is used id is always an int
139
-			$entity->setId((int)$qb->getLastInsertId());
140
-		}
141
-
142
-		return $entity;
143
-	}
144
-
145
-	/**
146
-	 * Tries to creates a new entry in the db from an entity and
147
-	 * updates an existing entry if duplicate keys are detected
148
-	 * by the database
149
-	 *
150
-	 * @param Entity $entity the entity that should be created/updated
151
-	 * @psalm-param T $entity the entity that should be created/updated
152
-	 * @return Entity the saved entity with the (new) id
153
-	 * @psalm-return T the saved entity with the (new) id
154
-	 * @throws \InvalidArgumentException if entity has no id
155
-	 * @since 15.0.0
156
-	 */
157
-	public function insertOrUpdate(Entity $entity): Entity {
158
-		try {
159
-			return $this->insert($entity);
160
-		} catch (UniqueConstraintViolationException $ex) {
161
-			return $this->update($entity);
162
-		}
163
-	}
164
-
165
-	/**
166
-	 * Updates an entry in the db from an entity
167
-	 * @throws \InvalidArgumentException if entity has no id
168
-	 * @param Entity $entity the entity that should be created
169
-	 * @psalm-param T $entity the entity that should be created
170
-	 * @return Entity the saved entity with the set id
171
-	 * @psalm-return T the saved entity with the set id
172
-	 * @since 14.0.0
173
-	 */
174
-	public function update(Entity $entity): Entity {
175
-		// if entity wasn't changed it makes no sense to run a db query
176
-		$properties = $entity->getUpdatedFields();
177
-		if (\count($properties) === 0) {
178
-			return $entity;
179
-		}
180
-
181
-		// entity needs an id
182
-		$id = $entity->getId();
183
-		if ($id === null) {
184
-			throw new \InvalidArgumentException(
185
-				'Entity which should be updated has no id');
186
-		}
187
-
188
-		// get updated fields to save, fields have to be set using a setter to
189
-		// be saved
190
-		// do not update the id field
191
-		unset($properties['id']);
192
-
193
-		$qb = $this->db->getQueryBuilder();
194
-		$qb->update($this->tableName);
195
-
196
-		// build the fields
197
-		foreach ($properties as $property => $updated) {
198
-			$column = $entity->propertyToColumn($property);
199
-			$getter = 'get' . ucfirst($property);
200
-			$value = $entity->$getter();
201
-
202
-			$type = $this->getParameterTypeForProperty($entity, $property);
203
-			$qb->set($column, $qb->createNamedParameter($value, $type));
204
-		}
205
-
206
-		$idType = $this->getParameterTypeForProperty($entity, 'id');
207
-
208
-		$qb->where(
209
-			$qb->expr()->eq('id', $qb->createNamedParameter($id, $idType))
210
-		);
211
-		$qb->execute();
212
-
213
-		return $entity;
214
-	}
215
-
216
-	/**
217
-	 * Returns the type parameter for the QueryBuilder for a specific property
218
-	 * of the $entity
219
-	 *
220
-	 * @param Entity $entity   The entity to get the types from
221
-	 * @psalm-param T $entity
222
-	 * @param string $property The property of $entity to get the type for
223
-	 * @return int
224
-	 * @since 16.0.0
225
-	 */
226
-	protected function getParameterTypeForProperty(Entity $entity, string $property): int {
227
-		$types = $entity->getFieldTypes();
228
-
229
-		if (!isset($types[ $property ])) {
230
-			return IQueryBuilder::PARAM_STR;
231
-		}
232
-
233
-		switch ($types[ $property ]) {
234
-			case 'int':
235
-			case 'integer':
236
-				return IQueryBuilder::PARAM_INT;
237
-			case 'string':
238
-				return IQueryBuilder::PARAM_STR;
239
-			case 'bool':
240
-			case 'boolean':
241
-				return IQueryBuilder::PARAM_BOOL;
242
-			case 'blob':
243
-				return IQueryBuilder::PARAM_LOB;
244
-		}
245
-
246
-		return IQueryBuilder::PARAM_STR;
247
-	}
248
-
249
-	/**
250
-	 * Returns an db result and throws exceptions when there are more or less
251
-	 * results
252
-	 *
253
-	 * @see findEntity
254
-	 *
255
-	 * @param IQueryBuilder $query
256
-	 * @throws DoesNotExistException if the item does not exist
257
-	 * @throws MultipleObjectsReturnedException if more than one item exist
258
-	 * @return array the result as row
259
-	 * @since 14.0.0
260
-	 */
261
-	protected function findOneQuery(IQueryBuilder $query): array {
262
-		$cursor = $query->execute();
263
-
264
-		$row = $cursor->fetch();
265
-		if ($row === false) {
266
-			$cursor->closeCursor();
267
-			$msg = $this->buildDebugMessage(
268
-				'Did expect one result but found none when executing', $query
269
-			);
270
-			throw new DoesNotExistException($msg);
271
-		}
272
-
273
-		$row2 = $cursor->fetch();
274
-		$cursor->closeCursor();
275
-		if ($row2 !== false) {
276
-			$msg = $this->buildDebugMessage(
277
-				'Did not expect more than one result when executing', $query
278
-			);
279
-			throw new MultipleObjectsReturnedException($msg);
280
-		}
281
-
282
-		return $row;
283
-	}
284
-
285
-	/**
286
-	 * @param string $msg
287
-	 * @param IQueryBuilder $sql
288
-	 * @return string
289
-	 * @since 14.0.0
290
-	 */
291
-	private function buildDebugMessage(string $msg, IQueryBuilder $sql): string {
292
-		return $msg .
293
-			': query "' . $sql->getSQL() . '"; ';
294
-	}
295
-
296
-
297
-	/**
298
-	 * Creates an entity from a row. Automatically determines the entity class
299
-	 * from the current mapper name (MyEntityMapper -> MyEntity)
300
-	 *
301
-	 * @param array $row the row which should be converted to an entity
302
-	 * @return Entity the entity
303
-	 * @psalm-return T the entity
304
-	 * @since 14.0.0
305
-	 */
306
-	protected function mapRowToEntity(array $row): Entity {
307
-		return \call_user_func($this->entityClass .'::fromRow', $row);
308
-	}
309
-
310
-
311
-	/**
312
-	 * Runs a sql query and returns an array of entities
313
-	 *
314
-	 * @param IQueryBuilder $query
315
-	 * @return Entity[] all fetched entities
316
-	 * @psalm-return T[] all fetched entities
317
-	 * @since 14.0.0
318
-	 */
319
-	protected function findEntities(IQueryBuilder $query): array {
320
-		$cursor = $query->execute();
321
-
322
-		$entities = [];
323
-
324
-		while ($row = $cursor->fetch()) {
325
-			$entities[] = $this->mapRowToEntity($row);
326
-		}
327
-
328
-		$cursor->closeCursor();
329
-
330
-		return $entities;
331
-	}
332
-
333
-
334
-	/**
335
-	 * Returns an db result and throws exceptions when there are more or less
336
-	 * results
337
-	 *
338
-	 * @param IQueryBuilder $query
339
-	 * @throws DoesNotExistException if the item does not exist
340
-	 * @throws MultipleObjectsReturnedException if more than one item exist
341
-	 * @return Entity the entity
342
-	 * @psalm-return T the entity
343
-	 * @since 14.0.0
344
-	 */
345
-	protected function findEntity(IQueryBuilder $query): Entity {
346
-		return $this->mapRowToEntity($this->findOneQuery($query));
347
-	}
47
+    /** @var string */
48
+    protected $tableName;
49
+
50
+    /** @var string|class-string<T> */
51
+    protected $entityClass;
52
+
53
+    /** @var IDBConnection */
54
+    protected $db;
55
+
56
+    /**
57
+     * @param IDBConnection $db Instance of the Db abstraction layer
58
+     * @param string $tableName the name of the table. set this to allow entity
59
+     * @param string|null $entityClass the name of the entity that the sql should be
60
+     * @psalm-param class-string<T>|null $entityClass the name of the entity that the sql should be
61
+     * mapped to queries without using sql
62
+     * @since 14.0.0
63
+     */
64
+    public function __construct(IDBConnection $db, string $tableName, string $entityClass = null) {
65
+        $this->db = $db;
66
+        $this->tableName = $tableName;
67
+
68
+        // if not given set the entity name to the class without the mapper part
69
+        // cache it here for later use since reflection is slow
70
+        if ($entityClass === null) {
71
+            $this->entityClass = str_replace('Mapper', '', \get_class($this));
72
+        } else {
73
+            $this->entityClass = $entityClass;
74
+        }
75
+    }
76
+
77
+
78
+    /**
79
+     * @return string the table name
80
+     * @since 14.0.0
81
+     */
82
+    public function getTableName(): string {
83
+        return $this->tableName;
84
+    }
85
+
86
+
87
+    /**
88
+     * Deletes an entity from the table
89
+     * @param Entity $entity the entity that should be deleted
90
+     * @psalm-param T $entity the entity that should be deleted
91
+     * @return Entity the deleted entity
92
+     * @psalm-return T the deleted entity
93
+     * @since 14.0.0
94
+     */
95
+    public function delete(Entity $entity): Entity {
96
+        $qb = $this->db->getQueryBuilder();
97
+
98
+        $idType = $this->getParameterTypeForProperty($entity, 'id');
99
+
100
+        $qb->delete($this->tableName)
101
+            ->where(
102
+                $qb->expr()->eq('id', $qb->createNamedParameter($entity->getId(), $idType))
103
+            );
104
+        $qb->execute();
105
+        return $entity;
106
+    }
107
+
108
+
109
+    /**
110
+     * Creates a new entry in the db from an entity
111
+     * @param Entity $entity the entity that should be created
112
+     * @psalm-param T $entity the entity that should be created
113
+     * @return Entity the saved entity with the set id
114
+     * @psalm-return T the saved entity with the set id
115
+     * @since 14.0.0
116
+     */
117
+    public function insert(Entity $entity): Entity {
118
+        // get updated fields to save, fields have to be set using a setter to
119
+        // be saved
120
+        $properties = $entity->getUpdatedFields();
121
+
122
+        $qb = $this->db->getQueryBuilder();
123
+        $qb->insert($this->tableName);
124
+
125
+        // build the fields
126
+        foreach ($properties as $property => $updated) {
127
+            $column = $entity->propertyToColumn($property);
128
+            $getter = 'get' . ucfirst($property);
129
+            $value = $entity->$getter();
130
+
131
+            $type = $this->getParameterTypeForProperty($entity, $property);
132
+            $qb->setValue($column, $qb->createNamedParameter($value, $type));
133
+        }
134
+
135
+        $qb->execute();
136
+
137
+        if ($entity->id === null) {
138
+            // When autoincrement is used id is always an int
139
+            $entity->setId((int)$qb->getLastInsertId());
140
+        }
141
+
142
+        return $entity;
143
+    }
144
+
145
+    /**
146
+     * Tries to creates a new entry in the db from an entity and
147
+     * updates an existing entry if duplicate keys are detected
148
+     * by the database
149
+     *
150
+     * @param Entity $entity the entity that should be created/updated
151
+     * @psalm-param T $entity the entity that should be created/updated
152
+     * @return Entity the saved entity with the (new) id
153
+     * @psalm-return T the saved entity with the (new) id
154
+     * @throws \InvalidArgumentException if entity has no id
155
+     * @since 15.0.0
156
+     */
157
+    public function insertOrUpdate(Entity $entity): Entity {
158
+        try {
159
+            return $this->insert($entity);
160
+        } catch (UniqueConstraintViolationException $ex) {
161
+            return $this->update($entity);
162
+        }
163
+    }
164
+
165
+    /**
166
+     * Updates an entry in the db from an entity
167
+     * @throws \InvalidArgumentException if entity has no id
168
+     * @param Entity $entity the entity that should be created
169
+     * @psalm-param T $entity the entity that should be created
170
+     * @return Entity the saved entity with the set id
171
+     * @psalm-return T the saved entity with the set id
172
+     * @since 14.0.0
173
+     */
174
+    public function update(Entity $entity): Entity {
175
+        // if entity wasn't changed it makes no sense to run a db query
176
+        $properties = $entity->getUpdatedFields();
177
+        if (\count($properties) === 0) {
178
+            return $entity;
179
+        }
180
+
181
+        // entity needs an id
182
+        $id = $entity->getId();
183
+        if ($id === null) {
184
+            throw new \InvalidArgumentException(
185
+                'Entity which should be updated has no id');
186
+        }
187
+
188
+        // get updated fields to save, fields have to be set using a setter to
189
+        // be saved
190
+        // do not update the id field
191
+        unset($properties['id']);
192
+
193
+        $qb = $this->db->getQueryBuilder();
194
+        $qb->update($this->tableName);
195
+
196
+        // build the fields
197
+        foreach ($properties as $property => $updated) {
198
+            $column = $entity->propertyToColumn($property);
199
+            $getter = 'get' . ucfirst($property);
200
+            $value = $entity->$getter();
201
+
202
+            $type = $this->getParameterTypeForProperty($entity, $property);
203
+            $qb->set($column, $qb->createNamedParameter($value, $type));
204
+        }
205
+
206
+        $idType = $this->getParameterTypeForProperty($entity, 'id');
207
+
208
+        $qb->where(
209
+            $qb->expr()->eq('id', $qb->createNamedParameter($id, $idType))
210
+        );
211
+        $qb->execute();
212
+
213
+        return $entity;
214
+    }
215
+
216
+    /**
217
+     * Returns the type parameter for the QueryBuilder for a specific property
218
+     * of the $entity
219
+     *
220
+     * @param Entity $entity   The entity to get the types from
221
+     * @psalm-param T $entity
222
+     * @param string $property The property of $entity to get the type for
223
+     * @return int
224
+     * @since 16.0.0
225
+     */
226
+    protected function getParameterTypeForProperty(Entity $entity, string $property): int {
227
+        $types = $entity->getFieldTypes();
228
+
229
+        if (!isset($types[ $property ])) {
230
+            return IQueryBuilder::PARAM_STR;
231
+        }
232
+
233
+        switch ($types[ $property ]) {
234
+            case 'int':
235
+            case 'integer':
236
+                return IQueryBuilder::PARAM_INT;
237
+            case 'string':
238
+                return IQueryBuilder::PARAM_STR;
239
+            case 'bool':
240
+            case 'boolean':
241
+                return IQueryBuilder::PARAM_BOOL;
242
+            case 'blob':
243
+                return IQueryBuilder::PARAM_LOB;
244
+        }
245
+
246
+        return IQueryBuilder::PARAM_STR;
247
+    }
248
+
249
+    /**
250
+     * Returns an db result and throws exceptions when there are more or less
251
+     * results
252
+     *
253
+     * @see findEntity
254
+     *
255
+     * @param IQueryBuilder $query
256
+     * @throws DoesNotExistException if the item does not exist
257
+     * @throws MultipleObjectsReturnedException if more than one item exist
258
+     * @return array the result as row
259
+     * @since 14.0.0
260
+     */
261
+    protected function findOneQuery(IQueryBuilder $query): array {
262
+        $cursor = $query->execute();
263
+
264
+        $row = $cursor->fetch();
265
+        if ($row === false) {
266
+            $cursor->closeCursor();
267
+            $msg = $this->buildDebugMessage(
268
+                'Did expect one result but found none when executing', $query
269
+            );
270
+            throw new DoesNotExistException($msg);
271
+        }
272
+
273
+        $row2 = $cursor->fetch();
274
+        $cursor->closeCursor();
275
+        if ($row2 !== false) {
276
+            $msg = $this->buildDebugMessage(
277
+                'Did not expect more than one result when executing', $query
278
+            );
279
+            throw new MultipleObjectsReturnedException($msg);
280
+        }
281
+
282
+        return $row;
283
+    }
284
+
285
+    /**
286
+     * @param string $msg
287
+     * @param IQueryBuilder $sql
288
+     * @return string
289
+     * @since 14.0.0
290
+     */
291
+    private function buildDebugMessage(string $msg, IQueryBuilder $sql): string {
292
+        return $msg .
293
+            ': query "' . $sql->getSQL() . '"; ';
294
+    }
295
+
296
+
297
+    /**
298
+     * Creates an entity from a row. Automatically determines the entity class
299
+     * from the current mapper name (MyEntityMapper -> MyEntity)
300
+     *
301
+     * @param array $row the row which should be converted to an entity
302
+     * @return Entity the entity
303
+     * @psalm-return T the entity
304
+     * @since 14.0.0
305
+     */
306
+    protected function mapRowToEntity(array $row): Entity {
307
+        return \call_user_func($this->entityClass .'::fromRow', $row);
308
+    }
309
+
310
+
311
+    /**
312
+     * Runs a sql query and returns an array of entities
313
+     *
314
+     * @param IQueryBuilder $query
315
+     * @return Entity[] all fetched entities
316
+     * @psalm-return T[] all fetched entities
317
+     * @since 14.0.0
318
+     */
319
+    protected function findEntities(IQueryBuilder $query): array {
320
+        $cursor = $query->execute();
321
+
322
+        $entities = [];
323
+
324
+        while ($row = $cursor->fetch()) {
325
+            $entities[] = $this->mapRowToEntity($row);
326
+        }
327
+
328
+        $cursor->closeCursor();
329
+
330
+        return $entities;
331
+    }
332
+
333
+
334
+    /**
335
+     * Returns an db result and throws exceptions when there are more or less
336
+     * results
337
+     *
338
+     * @param IQueryBuilder $query
339
+     * @throws DoesNotExistException if the item does not exist
340
+     * @throws MultipleObjectsReturnedException if more than one item exist
341
+     * @return Entity the entity
342
+     * @psalm-return T the entity
343
+     * @since 14.0.0
344
+     */
345
+    protected function findEntity(IQueryBuilder $query): Entity {
346
+        return $this->mapRowToEntity($this->findOneQuery($query));
347
+    }
348 348
 }
Please login to merge, or discard this patch.
lib/private/Authentication/WebAuthn/Db/PublicKeyCredentialMapper.php 1 patch
Indentation   +41 added lines, -41 removed lines patch added patch discarded remove patch
@@ -34,54 +34,54 @@
 block discarded – undo
34 34
  * @template-extends QBMapper<PublicKeyCredentialEntity>
35 35
  */
36 36
 class PublicKeyCredentialMapper extends QBMapper {
37
-	public function __construct(IDBConnection $db) {
38
-		parent::__construct($db, 'webauthn', PublicKeyCredentialEntity::class);
39
-	}
37
+    public function __construct(IDBConnection $db) {
38
+        parent::__construct($db, 'webauthn', PublicKeyCredentialEntity::class);
39
+    }
40 40
 
41
-	public function findOneByCredentialId(string $publicKeyCredentialId): PublicKeyCredentialEntity {
42
-		$qb = $this->db->getQueryBuilder();
41
+    public function findOneByCredentialId(string $publicKeyCredentialId): PublicKeyCredentialEntity {
42
+        $qb = $this->db->getQueryBuilder();
43 43
 
44
-		$qb->select('*')
45
-			->from($this->getTableName())
46
-			->where(
47
-				$qb->expr()->eq('public_key_credential_id', $qb->createNamedParameter(base64_encode($publicKeyCredentialId)))
48
-			);
44
+        $qb->select('*')
45
+            ->from($this->getTableName())
46
+            ->where(
47
+                $qb->expr()->eq('public_key_credential_id', $qb->createNamedParameter(base64_encode($publicKeyCredentialId)))
48
+            );
49 49
 
50
-		return $this->findEntity($qb);
51
-	}
50
+        return $this->findEntity($qb);
51
+    }
52 52
 
53
-	/**
54
-	 * @return PublicKeyCredentialEntity[]
55
-	 */
56
-	public function findAllForUid(string $uid): array {
57
-		$qb = $this->db->getQueryBuilder();
53
+    /**
54
+     * @return PublicKeyCredentialEntity[]
55
+     */
56
+    public function findAllForUid(string $uid): array {
57
+        $qb = $this->db->getQueryBuilder();
58 58
 
59
-		$qb->select('*')
60
-			->from($this->getTableName())
61
-			->where(
62
-				$qb->expr()->eq('uid', $qb->createNamedParameter($uid))
63
-			);
59
+        $qb->select('*')
60
+            ->from($this->getTableName())
61
+            ->where(
62
+                $qb->expr()->eq('uid', $qb->createNamedParameter($uid))
63
+            );
64 64
 
65
-		return $this->findEntities($qb);
66
-	}
65
+        return $this->findEntities($qb);
66
+    }
67 67
 
68
-	/**
69
-	 * @param string $uid
70
-	 * @param int $id
71
-	 *
72
-	 * @return PublicKeyCredentialEntity
73
-	 * @throws DoesNotExistException
74
-	 */
75
-	public function findById(string $uid, int $id): PublicKeyCredentialEntity {
76
-		$qb = $this->db->getQueryBuilder();
68
+    /**
69
+     * @param string $uid
70
+     * @param int $id
71
+     *
72
+     * @return PublicKeyCredentialEntity
73
+     * @throws DoesNotExistException
74
+     */
75
+    public function findById(string $uid, int $id): PublicKeyCredentialEntity {
76
+        $qb = $this->db->getQueryBuilder();
77 77
 
78
-		$qb->select('*')
79
-			->from($this->getTableName())
80
-			->where($qb->expr()->andX(
81
-				$qb->expr()->eq('id', $qb->createNamedParameter($id)),
82
-				$qb->expr()->eq('uid', $qb->createNamedParameter($uid))
83
-			));
78
+        $qb->select('*')
79
+            ->from($this->getTableName())
80
+            ->where($qb->expr()->andX(
81
+                $qb->expr()->eq('id', $qb->createNamedParameter($id)),
82
+                $qb->expr()->eq('uid', $qb->createNamedParameter($uid))
83
+            ));
84 84
 
85
-		return $this->findEntity($qb);
86
-	}
85
+        return $this->findEntity($qb);
86
+    }
87 87
 }
Please login to merge, or discard this patch.
lib/private/Authentication/Token/PublicKeyTokenProvider.php 1 patch
Indentation   +393 added lines, -393 removed lines patch added patch discarded remove patch
@@ -43,397 +43,397 @@
 block discarded – undo
43 43
 use OCP\Security\ICrypto;
44 44
 
45 45
 class PublicKeyTokenProvider implements IProvider {
46
-	/** @var PublicKeyTokenMapper */
47
-	private $mapper;
48
-
49
-	/** @var ICrypto */
50
-	private $crypto;
51
-
52
-	/** @var IConfig */
53
-	private $config;
54
-
55
-	/** @var ILogger $logger */
56
-	private $logger;
57
-
58
-	/** @var ITimeFactory $time */
59
-	private $time;
60
-
61
-	/** @var CappedMemoryCache */
62
-	private $cache;
63
-
64
-	public function __construct(PublicKeyTokenMapper $mapper,
65
-								ICrypto $crypto,
66
-								IConfig $config,
67
-								ILogger $logger,
68
-								ITimeFactory $time) {
69
-		$this->mapper = $mapper;
70
-		$this->crypto = $crypto;
71
-		$this->config = $config;
72
-		$this->logger = $logger;
73
-		$this->time = $time;
74
-
75
-		$this->cache = new CappedMemoryCache();
76
-	}
77
-
78
-	/**
79
-	 * {@inheritDoc}
80
-	 */
81
-	public function generateToken(string $token,
82
-								  string $uid,
83
-								  string $loginName,
84
-								  $password,
85
-								  string $name,
86
-								  int $type = IToken::TEMPORARY_TOKEN,
87
-								  int $remember = IToken::DO_NOT_REMEMBER): IToken {
88
-		$dbToken = $this->newToken($token, $uid, $loginName, $password, $name, $type, $remember);
89
-		$this->mapper->insert($dbToken);
90
-
91
-		// Add the token to the cache
92
-		$this->cache[$dbToken->getToken()] = $dbToken;
93
-
94
-		return $dbToken;
95
-	}
96
-
97
-	public function getToken(string $tokenId): IToken {
98
-		$tokenHash = $this->hashToken($tokenId);
99
-
100
-		if (isset($this->cache[$tokenHash])) {
101
-			$token = $this->cache[$tokenHash];
102
-		} else {
103
-			try {
104
-				$token = $this->mapper->getToken($this->hashToken($tokenId));
105
-				$this->cache[$token->getToken()] = $token;
106
-			} catch (DoesNotExistException $ex) {
107
-				throw new InvalidTokenException("Token does not exist: " . $ex->getMessage(), 0, $ex);
108
-			}
109
-		}
110
-
111
-		if ((int)$token->getExpires() !== 0 && $token->getExpires() < $this->time->getTime()) {
112
-			throw new ExpiredTokenException($token);
113
-		}
114
-
115
-		if ($token->getType() === IToken::WIPE_TOKEN) {
116
-			throw new WipeTokenException($token);
117
-		}
118
-
119
-		if ($token->getPasswordInvalid() === true) {
120
-			//The password is invalid we should throw an TokenPasswordExpiredException
121
-			throw new TokenPasswordExpiredException($token);
122
-		}
123
-
124
-		return $token;
125
-	}
126
-
127
-	public function getTokenById(int $tokenId): IToken {
128
-		try {
129
-			$token = $this->mapper->getTokenById($tokenId);
130
-		} catch (DoesNotExistException $ex) {
131
-			throw new InvalidTokenException("Token with ID $tokenId does not exist: " . $ex->getMessage(), 0, $ex);
132
-		}
133
-
134
-		if ((int)$token->getExpires() !== 0 && $token->getExpires() < $this->time->getTime()) {
135
-			throw new ExpiredTokenException($token);
136
-		}
137
-
138
-		if ($token->getType() === IToken::WIPE_TOKEN) {
139
-			throw new WipeTokenException($token);
140
-		}
141
-
142
-		if ($token->getPasswordInvalid() === true) {
143
-			//The password is invalid we should throw an TokenPasswordExpiredException
144
-			throw new TokenPasswordExpiredException($token);
145
-		}
146
-
147
-		return $token;
148
-	}
149
-
150
-	public function renewSessionToken(string $oldSessionId, string $sessionId): IToken {
151
-		$this->cache->clear();
152
-
153
-		$token = $this->getToken($oldSessionId);
154
-
155
-		if (!($token instanceof PublicKeyToken)) {
156
-			throw new InvalidTokenException("Invalid token type");
157
-		}
158
-
159
-		$password = null;
160
-		if (!is_null($token->getPassword())) {
161
-			$privateKey = $this->decrypt($token->getPrivateKey(), $oldSessionId);
162
-			$password = $this->decryptPassword($token->getPassword(), $privateKey);
163
-		}
164
-
165
-		$newToken = $this->generateToken(
166
-			$sessionId,
167
-			$token->getUID(),
168
-			$token->getLoginName(),
169
-			$password,
170
-			$token->getName(),
171
-			IToken::TEMPORARY_TOKEN,
172
-			$token->getRemember()
173
-		);
174
-
175
-		$this->mapper->delete($token);
176
-
177
-		return $newToken;
178
-	}
179
-
180
-	public function invalidateToken(string $token) {
181
-		$this->cache->clear();
182
-
183
-		$this->mapper->invalidate($this->hashToken($token));
184
-	}
185
-
186
-	public function invalidateTokenById(string $uid, int $id) {
187
-		$this->cache->clear();
188
-
189
-		$this->mapper->deleteById($uid, $id);
190
-	}
191
-
192
-	public function invalidateOldTokens() {
193
-		$this->cache->clear();
194
-
195
-		$olderThan = $this->time->getTime() - (int) $this->config->getSystemValue('session_lifetime', 60 * 60 * 24);
196
-		$this->logger->debug('Invalidating session tokens older than ' . date('c', $olderThan), ['app' => 'cron']);
197
-		$this->mapper->invalidateOld($olderThan, IToken::DO_NOT_REMEMBER);
198
-		$rememberThreshold = $this->time->getTime() - (int) $this->config->getSystemValue('remember_login_cookie_lifetime', 60 * 60 * 24 * 15);
199
-		$this->logger->debug('Invalidating remembered session tokens older than ' . date('c', $rememberThreshold), ['app' => 'cron']);
200
-		$this->mapper->invalidateOld($rememberThreshold, IToken::REMEMBER);
201
-	}
202
-
203
-	public function updateToken(IToken $token) {
204
-		$this->cache->clear();
205
-
206
-		if (!($token instanceof PublicKeyToken)) {
207
-			throw new InvalidTokenException("Invalid token type");
208
-		}
209
-		$this->mapper->update($token);
210
-	}
211
-
212
-	public function updateTokenActivity(IToken $token) {
213
-		$this->cache->clear();
214
-
215
-		if (!($token instanceof PublicKeyToken)) {
216
-			throw new InvalidTokenException("Invalid token type");
217
-		}
218
-
219
-		$activityInterval = $this->config->getSystemValueInt('token_auth_activity_update', 60);
220
-		$activityInterval = min(max($activityInterval, 0), 300);
221
-
222
-		/** @var PublicKeyToken $token */
223
-		$now = $this->time->getTime();
224
-		if ($token->getLastActivity() < ($now - $activityInterval)) {
225
-			// Update token only once per minute
226
-			$token->setLastActivity($now);
227
-			$this->mapper->update($token);
228
-		}
229
-	}
230
-
231
-	public function getTokenByUser(string $uid): array {
232
-		return $this->mapper->getTokenByUser($uid);
233
-	}
234
-
235
-	public function getPassword(IToken $savedToken, string $tokenId): string {
236
-		if (!($savedToken instanceof PublicKeyToken)) {
237
-			throw new InvalidTokenException("Invalid token type");
238
-		}
239
-
240
-		if ($savedToken->getPassword() === null) {
241
-			throw new PasswordlessTokenException();
242
-		}
243
-
244
-		// Decrypt private key with tokenId
245
-		$privateKey = $this->decrypt($savedToken->getPrivateKey(), $tokenId);
246
-
247
-		// Decrypt password with private key
248
-		return $this->decryptPassword($savedToken->getPassword(), $privateKey);
249
-	}
250
-
251
-	public function setPassword(IToken $token, string $tokenId, string $password) {
252
-		$this->cache->clear();
253
-
254
-		if (!($token instanceof PublicKeyToken)) {
255
-			throw new InvalidTokenException("Invalid token type");
256
-		}
257
-
258
-		// When changing passwords all temp tokens are deleted
259
-		$this->mapper->deleteTempToken($token);
260
-
261
-		// Update the password for all tokens
262
-		$tokens = $this->mapper->getTokenByUser($token->getUID());
263
-		foreach ($tokens as $t) {
264
-			$publicKey = $t->getPublicKey();
265
-			$t->setPassword($this->encryptPassword($password, $publicKey));
266
-			$this->updateToken($t);
267
-		}
268
-	}
269
-
270
-	public function rotate(IToken $token, string $oldTokenId, string $newTokenId): IToken {
271
-		$this->cache->clear();
272
-
273
-		if (!($token instanceof PublicKeyToken)) {
274
-			throw new InvalidTokenException("Invalid token type");
275
-		}
276
-
277
-		// Decrypt private key with oldTokenId
278
-		$privateKey = $this->decrypt($token->getPrivateKey(), $oldTokenId);
279
-		// Encrypt with the new token
280
-		$token->setPrivateKey($this->encrypt($privateKey, $newTokenId));
281
-
282
-		$token->setToken($this->hashToken($newTokenId));
283
-		$this->updateToken($token);
284
-
285
-		return $token;
286
-	}
287
-
288
-	private function encrypt(string $plaintext, string $token): string {
289
-		$secret = $this->config->getSystemValue('secret');
290
-		return $this->crypto->encrypt($plaintext, $token . $secret);
291
-	}
292
-
293
-	/**
294
-	 * @throws InvalidTokenException
295
-	 */
296
-	private function decrypt(string $cipherText, string $token): string {
297
-		$secret = $this->config->getSystemValue('secret');
298
-		try {
299
-			return $this->crypto->decrypt($cipherText, $token . $secret);
300
-		} catch (\Exception $ex) {
301
-			// Delete the invalid token
302
-			$this->invalidateToken($token);
303
-			throw new InvalidTokenException("Could not decrypt token password: " . $ex->getMessage(), 0, $ex);
304
-		}
305
-	}
306
-
307
-	private function encryptPassword(string $password, string $publicKey): string {
308
-		openssl_public_encrypt($password, $encryptedPassword, $publicKey, OPENSSL_PKCS1_OAEP_PADDING);
309
-		$encryptedPassword = base64_encode($encryptedPassword);
310
-
311
-		return $encryptedPassword;
312
-	}
313
-
314
-	private function decryptPassword(string $encryptedPassword, string $privateKey): string {
315
-		$encryptedPassword = base64_decode($encryptedPassword);
316
-		openssl_private_decrypt($encryptedPassword, $password, $privateKey, OPENSSL_PKCS1_OAEP_PADDING);
317
-
318
-		return $password;
319
-	}
320
-
321
-	private function hashToken(string $token): string {
322
-		$secret = $this->config->getSystemValue('secret');
323
-		return hash('sha512', $token . $secret);
324
-	}
325
-
326
-	/**
327
-	 * Convert a DefaultToken to a publicKeyToken
328
-	 * This will also be updated directly in the Database
329
-	 * @throws \RuntimeException when OpenSSL reports a problem
330
-	 */
331
-	public function convertToken(DefaultToken $defaultToken, string $token, $password): PublicKeyToken {
332
-		$this->cache->clear();
333
-
334
-		$pkToken = $this->newToken(
335
-			$token,
336
-			$defaultToken->getUID(),
337
-			$defaultToken->getLoginName(),
338
-			$password,
339
-			$defaultToken->getName(),
340
-			$defaultToken->getType(),
341
-			$defaultToken->getRemember()
342
-		);
343
-
344
-		$pkToken->setExpires($defaultToken->getExpires());
345
-		$pkToken->setId($defaultToken->getId());
346
-
347
-		return $this->mapper->update($pkToken);
348
-	}
349
-
350
-	/**
351
-	 * @throws \RuntimeException when OpenSSL reports a problem
352
-	 */
353
-	private function newToken(string $token,
354
-							  string $uid,
355
-							  string $loginName,
356
-							  $password,
357
-							  string $name,
358
-							  int $type,
359
-							  int $remember): PublicKeyToken {
360
-		$dbToken = new PublicKeyToken();
361
-		$dbToken->setUid($uid);
362
-		$dbToken->setLoginName($loginName);
363
-
364
-		$config = array_merge([
365
-			'digest_alg' => 'sha512',
366
-			'private_key_bits' => 2048,
367
-		], $this->config->getSystemValue('openssl', []));
368
-
369
-		// Generate new key
370
-		$res = openssl_pkey_new($config);
371
-		if ($res === false) {
372
-			$this->logOpensslError();
373
-			throw new \RuntimeException('OpenSSL reported a problem');
374
-		}
375
-
376
-		if (openssl_pkey_export($res, $privateKey, null, $config) === false) {
377
-			$this->logOpensslError();
378
-			throw new \RuntimeException('OpenSSL reported a problem');
379
-		}
380
-
381
-		// Extract the public key from $res to $pubKey
382
-		$publicKey = openssl_pkey_get_details($res);
383
-		$publicKey = $publicKey['key'];
384
-
385
-		$dbToken->setPublicKey($publicKey);
386
-		$dbToken->setPrivateKey($this->encrypt($privateKey, $token));
387
-
388
-		if (!is_null($password)) {
389
-			$dbToken->setPassword($this->encryptPassword($password, $publicKey));
390
-		}
391
-
392
-		$dbToken->setName($name);
393
-		$dbToken->setToken($this->hashToken($token));
394
-		$dbToken->setType($type);
395
-		$dbToken->setRemember($remember);
396
-		$dbToken->setLastActivity($this->time->getTime());
397
-		$dbToken->setLastCheck($this->time->getTime());
398
-		$dbToken->setVersion(PublicKeyToken::VERSION);
399
-
400
-		return $dbToken;
401
-	}
402
-
403
-	public function markPasswordInvalid(IToken $token, string $tokenId) {
404
-		$this->cache->clear();
405
-
406
-		if (!($token instanceof PublicKeyToken)) {
407
-			throw new InvalidTokenException("Invalid token type");
408
-		}
409
-
410
-		$token->setPasswordInvalid(true);
411
-		$this->mapper->update($token);
412
-	}
413
-
414
-	public function updatePasswords(string $uid, string $password) {
415
-		$this->cache->clear();
416
-
417
-		if (!$this->mapper->hasExpiredTokens($uid)) {
418
-			// Nothing to do here
419
-			return;
420
-		}
421
-
422
-		// Update the password for all tokens
423
-		$tokens = $this->mapper->getTokenByUser($uid);
424
-		foreach ($tokens as $t) {
425
-			$publicKey = $t->getPublicKey();
426
-			$t->setPassword($this->encryptPassword($password, $publicKey));
427
-			$t->setPasswordInvalid(false);
428
-			$this->updateToken($t);
429
-		}
430
-	}
431
-
432
-	private function logOpensslError() {
433
-		$errors = [];
434
-		while ($error = openssl_error_string()) {
435
-			$errors[] = $error;
436
-		}
437
-		$this->logger->critical('Something is wrong with your openssl setup: ' . implode(', ', $errors));
438
-	}
46
+    /** @var PublicKeyTokenMapper */
47
+    private $mapper;
48
+
49
+    /** @var ICrypto */
50
+    private $crypto;
51
+
52
+    /** @var IConfig */
53
+    private $config;
54
+
55
+    /** @var ILogger $logger */
56
+    private $logger;
57
+
58
+    /** @var ITimeFactory $time */
59
+    private $time;
60
+
61
+    /** @var CappedMemoryCache */
62
+    private $cache;
63
+
64
+    public function __construct(PublicKeyTokenMapper $mapper,
65
+                                ICrypto $crypto,
66
+                                IConfig $config,
67
+                                ILogger $logger,
68
+                                ITimeFactory $time) {
69
+        $this->mapper = $mapper;
70
+        $this->crypto = $crypto;
71
+        $this->config = $config;
72
+        $this->logger = $logger;
73
+        $this->time = $time;
74
+
75
+        $this->cache = new CappedMemoryCache();
76
+    }
77
+
78
+    /**
79
+     * {@inheritDoc}
80
+     */
81
+    public function generateToken(string $token,
82
+                                    string $uid,
83
+                                    string $loginName,
84
+                                    $password,
85
+                                    string $name,
86
+                                    int $type = IToken::TEMPORARY_TOKEN,
87
+                                    int $remember = IToken::DO_NOT_REMEMBER): IToken {
88
+        $dbToken = $this->newToken($token, $uid, $loginName, $password, $name, $type, $remember);
89
+        $this->mapper->insert($dbToken);
90
+
91
+        // Add the token to the cache
92
+        $this->cache[$dbToken->getToken()] = $dbToken;
93
+
94
+        return $dbToken;
95
+    }
96
+
97
+    public function getToken(string $tokenId): IToken {
98
+        $tokenHash = $this->hashToken($tokenId);
99
+
100
+        if (isset($this->cache[$tokenHash])) {
101
+            $token = $this->cache[$tokenHash];
102
+        } else {
103
+            try {
104
+                $token = $this->mapper->getToken($this->hashToken($tokenId));
105
+                $this->cache[$token->getToken()] = $token;
106
+            } catch (DoesNotExistException $ex) {
107
+                throw new InvalidTokenException("Token does not exist: " . $ex->getMessage(), 0, $ex);
108
+            }
109
+        }
110
+
111
+        if ((int)$token->getExpires() !== 0 && $token->getExpires() < $this->time->getTime()) {
112
+            throw new ExpiredTokenException($token);
113
+        }
114
+
115
+        if ($token->getType() === IToken::WIPE_TOKEN) {
116
+            throw new WipeTokenException($token);
117
+        }
118
+
119
+        if ($token->getPasswordInvalid() === true) {
120
+            //The password is invalid we should throw an TokenPasswordExpiredException
121
+            throw new TokenPasswordExpiredException($token);
122
+        }
123
+
124
+        return $token;
125
+    }
126
+
127
+    public function getTokenById(int $tokenId): IToken {
128
+        try {
129
+            $token = $this->mapper->getTokenById($tokenId);
130
+        } catch (DoesNotExistException $ex) {
131
+            throw new InvalidTokenException("Token with ID $tokenId does not exist: " . $ex->getMessage(), 0, $ex);
132
+        }
133
+
134
+        if ((int)$token->getExpires() !== 0 && $token->getExpires() < $this->time->getTime()) {
135
+            throw new ExpiredTokenException($token);
136
+        }
137
+
138
+        if ($token->getType() === IToken::WIPE_TOKEN) {
139
+            throw new WipeTokenException($token);
140
+        }
141
+
142
+        if ($token->getPasswordInvalid() === true) {
143
+            //The password is invalid we should throw an TokenPasswordExpiredException
144
+            throw new TokenPasswordExpiredException($token);
145
+        }
146
+
147
+        return $token;
148
+    }
149
+
150
+    public function renewSessionToken(string $oldSessionId, string $sessionId): IToken {
151
+        $this->cache->clear();
152
+
153
+        $token = $this->getToken($oldSessionId);
154
+
155
+        if (!($token instanceof PublicKeyToken)) {
156
+            throw new InvalidTokenException("Invalid token type");
157
+        }
158
+
159
+        $password = null;
160
+        if (!is_null($token->getPassword())) {
161
+            $privateKey = $this->decrypt($token->getPrivateKey(), $oldSessionId);
162
+            $password = $this->decryptPassword($token->getPassword(), $privateKey);
163
+        }
164
+
165
+        $newToken = $this->generateToken(
166
+            $sessionId,
167
+            $token->getUID(),
168
+            $token->getLoginName(),
169
+            $password,
170
+            $token->getName(),
171
+            IToken::TEMPORARY_TOKEN,
172
+            $token->getRemember()
173
+        );
174
+
175
+        $this->mapper->delete($token);
176
+
177
+        return $newToken;
178
+    }
179
+
180
+    public function invalidateToken(string $token) {
181
+        $this->cache->clear();
182
+
183
+        $this->mapper->invalidate($this->hashToken($token));
184
+    }
185
+
186
+    public function invalidateTokenById(string $uid, int $id) {
187
+        $this->cache->clear();
188
+
189
+        $this->mapper->deleteById($uid, $id);
190
+    }
191
+
192
+    public function invalidateOldTokens() {
193
+        $this->cache->clear();
194
+
195
+        $olderThan = $this->time->getTime() - (int) $this->config->getSystemValue('session_lifetime', 60 * 60 * 24);
196
+        $this->logger->debug('Invalidating session tokens older than ' . date('c', $olderThan), ['app' => 'cron']);
197
+        $this->mapper->invalidateOld($olderThan, IToken::DO_NOT_REMEMBER);
198
+        $rememberThreshold = $this->time->getTime() - (int) $this->config->getSystemValue('remember_login_cookie_lifetime', 60 * 60 * 24 * 15);
199
+        $this->logger->debug('Invalidating remembered session tokens older than ' . date('c', $rememberThreshold), ['app' => 'cron']);
200
+        $this->mapper->invalidateOld($rememberThreshold, IToken::REMEMBER);
201
+    }
202
+
203
+    public function updateToken(IToken $token) {
204
+        $this->cache->clear();
205
+
206
+        if (!($token instanceof PublicKeyToken)) {
207
+            throw new InvalidTokenException("Invalid token type");
208
+        }
209
+        $this->mapper->update($token);
210
+    }
211
+
212
+    public function updateTokenActivity(IToken $token) {
213
+        $this->cache->clear();
214
+
215
+        if (!($token instanceof PublicKeyToken)) {
216
+            throw new InvalidTokenException("Invalid token type");
217
+        }
218
+
219
+        $activityInterval = $this->config->getSystemValueInt('token_auth_activity_update', 60);
220
+        $activityInterval = min(max($activityInterval, 0), 300);
221
+
222
+        /** @var PublicKeyToken $token */
223
+        $now = $this->time->getTime();
224
+        if ($token->getLastActivity() < ($now - $activityInterval)) {
225
+            // Update token only once per minute
226
+            $token->setLastActivity($now);
227
+            $this->mapper->update($token);
228
+        }
229
+    }
230
+
231
+    public function getTokenByUser(string $uid): array {
232
+        return $this->mapper->getTokenByUser($uid);
233
+    }
234
+
235
+    public function getPassword(IToken $savedToken, string $tokenId): string {
236
+        if (!($savedToken instanceof PublicKeyToken)) {
237
+            throw new InvalidTokenException("Invalid token type");
238
+        }
239
+
240
+        if ($savedToken->getPassword() === null) {
241
+            throw new PasswordlessTokenException();
242
+        }
243
+
244
+        // Decrypt private key with tokenId
245
+        $privateKey = $this->decrypt($savedToken->getPrivateKey(), $tokenId);
246
+
247
+        // Decrypt password with private key
248
+        return $this->decryptPassword($savedToken->getPassword(), $privateKey);
249
+    }
250
+
251
+    public function setPassword(IToken $token, string $tokenId, string $password) {
252
+        $this->cache->clear();
253
+
254
+        if (!($token instanceof PublicKeyToken)) {
255
+            throw new InvalidTokenException("Invalid token type");
256
+        }
257
+
258
+        // When changing passwords all temp tokens are deleted
259
+        $this->mapper->deleteTempToken($token);
260
+
261
+        // Update the password for all tokens
262
+        $tokens = $this->mapper->getTokenByUser($token->getUID());
263
+        foreach ($tokens as $t) {
264
+            $publicKey = $t->getPublicKey();
265
+            $t->setPassword($this->encryptPassword($password, $publicKey));
266
+            $this->updateToken($t);
267
+        }
268
+    }
269
+
270
+    public function rotate(IToken $token, string $oldTokenId, string $newTokenId): IToken {
271
+        $this->cache->clear();
272
+
273
+        if (!($token instanceof PublicKeyToken)) {
274
+            throw new InvalidTokenException("Invalid token type");
275
+        }
276
+
277
+        // Decrypt private key with oldTokenId
278
+        $privateKey = $this->decrypt($token->getPrivateKey(), $oldTokenId);
279
+        // Encrypt with the new token
280
+        $token->setPrivateKey($this->encrypt($privateKey, $newTokenId));
281
+
282
+        $token->setToken($this->hashToken($newTokenId));
283
+        $this->updateToken($token);
284
+
285
+        return $token;
286
+    }
287
+
288
+    private function encrypt(string $plaintext, string $token): string {
289
+        $secret = $this->config->getSystemValue('secret');
290
+        return $this->crypto->encrypt($plaintext, $token . $secret);
291
+    }
292
+
293
+    /**
294
+     * @throws InvalidTokenException
295
+     */
296
+    private function decrypt(string $cipherText, string $token): string {
297
+        $secret = $this->config->getSystemValue('secret');
298
+        try {
299
+            return $this->crypto->decrypt($cipherText, $token . $secret);
300
+        } catch (\Exception $ex) {
301
+            // Delete the invalid token
302
+            $this->invalidateToken($token);
303
+            throw new InvalidTokenException("Could not decrypt token password: " . $ex->getMessage(), 0, $ex);
304
+        }
305
+    }
306
+
307
+    private function encryptPassword(string $password, string $publicKey): string {
308
+        openssl_public_encrypt($password, $encryptedPassword, $publicKey, OPENSSL_PKCS1_OAEP_PADDING);
309
+        $encryptedPassword = base64_encode($encryptedPassword);
310
+
311
+        return $encryptedPassword;
312
+    }
313
+
314
+    private function decryptPassword(string $encryptedPassword, string $privateKey): string {
315
+        $encryptedPassword = base64_decode($encryptedPassword);
316
+        openssl_private_decrypt($encryptedPassword, $password, $privateKey, OPENSSL_PKCS1_OAEP_PADDING);
317
+
318
+        return $password;
319
+    }
320
+
321
+    private function hashToken(string $token): string {
322
+        $secret = $this->config->getSystemValue('secret');
323
+        return hash('sha512', $token . $secret);
324
+    }
325
+
326
+    /**
327
+     * Convert a DefaultToken to a publicKeyToken
328
+     * This will also be updated directly in the Database
329
+     * @throws \RuntimeException when OpenSSL reports a problem
330
+     */
331
+    public function convertToken(DefaultToken $defaultToken, string $token, $password): PublicKeyToken {
332
+        $this->cache->clear();
333
+
334
+        $pkToken = $this->newToken(
335
+            $token,
336
+            $defaultToken->getUID(),
337
+            $defaultToken->getLoginName(),
338
+            $password,
339
+            $defaultToken->getName(),
340
+            $defaultToken->getType(),
341
+            $defaultToken->getRemember()
342
+        );
343
+
344
+        $pkToken->setExpires($defaultToken->getExpires());
345
+        $pkToken->setId($defaultToken->getId());
346
+
347
+        return $this->mapper->update($pkToken);
348
+    }
349
+
350
+    /**
351
+     * @throws \RuntimeException when OpenSSL reports a problem
352
+     */
353
+    private function newToken(string $token,
354
+                                string $uid,
355
+                                string $loginName,
356
+                                $password,
357
+                                string $name,
358
+                                int $type,
359
+                                int $remember): PublicKeyToken {
360
+        $dbToken = new PublicKeyToken();
361
+        $dbToken->setUid($uid);
362
+        $dbToken->setLoginName($loginName);
363
+
364
+        $config = array_merge([
365
+            'digest_alg' => 'sha512',
366
+            'private_key_bits' => 2048,
367
+        ], $this->config->getSystemValue('openssl', []));
368
+
369
+        // Generate new key
370
+        $res = openssl_pkey_new($config);
371
+        if ($res === false) {
372
+            $this->logOpensslError();
373
+            throw new \RuntimeException('OpenSSL reported a problem');
374
+        }
375
+
376
+        if (openssl_pkey_export($res, $privateKey, null, $config) === false) {
377
+            $this->logOpensslError();
378
+            throw new \RuntimeException('OpenSSL reported a problem');
379
+        }
380
+
381
+        // Extract the public key from $res to $pubKey
382
+        $publicKey = openssl_pkey_get_details($res);
383
+        $publicKey = $publicKey['key'];
384
+
385
+        $dbToken->setPublicKey($publicKey);
386
+        $dbToken->setPrivateKey($this->encrypt($privateKey, $token));
387
+
388
+        if (!is_null($password)) {
389
+            $dbToken->setPassword($this->encryptPassword($password, $publicKey));
390
+        }
391
+
392
+        $dbToken->setName($name);
393
+        $dbToken->setToken($this->hashToken($token));
394
+        $dbToken->setType($type);
395
+        $dbToken->setRemember($remember);
396
+        $dbToken->setLastActivity($this->time->getTime());
397
+        $dbToken->setLastCheck($this->time->getTime());
398
+        $dbToken->setVersion(PublicKeyToken::VERSION);
399
+
400
+        return $dbToken;
401
+    }
402
+
403
+    public function markPasswordInvalid(IToken $token, string $tokenId) {
404
+        $this->cache->clear();
405
+
406
+        if (!($token instanceof PublicKeyToken)) {
407
+            throw new InvalidTokenException("Invalid token type");
408
+        }
409
+
410
+        $token->setPasswordInvalid(true);
411
+        $this->mapper->update($token);
412
+    }
413
+
414
+    public function updatePasswords(string $uid, string $password) {
415
+        $this->cache->clear();
416
+
417
+        if (!$this->mapper->hasExpiredTokens($uid)) {
418
+            // Nothing to do here
419
+            return;
420
+        }
421
+
422
+        // Update the password for all tokens
423
+        $tokens = $this->mapper->getTokenByUser($uid);
424
+        foreach ($tokens as $t) {
425
+            $publicKey = $t->getPublicKey();
426
+            $t->setPassword($this->encryptPassword($password, $publicKey));
427
+            $t->setPasswordInvalid(false);
428
+            $this->updateToken($t);
429
+        }
430
+    }
431
+
432
+    private function logOpensslError() {
433
+        $errors = [];
434
+        while ($error = openssl_error_string()) {
435
+            $errors[] = $error;
436
+        }
437
+        $this->logger->critical('Something is wrong with your openssl setup: ' . implode(', ', $errors));
438
+    }
439 439
 }
Please login to merge, or discard this patch.
lib/private/Authentication/Token/PublicKeyTokenMapper.php 1 patch
Indentation   +154 added lines, -154 removed lines patch added patch discarded remove patch
@@ -36,158 +36,158 @@
 block discarded – undo
36 36
  * @template-extends QBMapper<PublicKeyToken>
37 37
  */
38 38
 class PublicKeyTokenMapper extends QBMapper {
39
-	public function __construct(IDBConnection $db) {
40
-		parent::__construct($db, 'authtoken');
41
-	}
42
-
43
-	/**
44
-	 * Invalidate (delete) a given token
45
-	 *
46
-	 * @param string $token
47
-	 */
48
-	public function invalidate(string $token) {
49
-		/* @var $qb IQueryBuilder */
50
-		$qb = $this->db->getQueryBuilder();
51
-		$qb->delete('authtoken')
52
-			->where($qb->expr()->eq('token', $qb->createNamedParameter($token)))
53
-			->andWhere($qb->expr()->eq('version', $qb->createNamedParameter(PublicKeyToken::VERSION, IQueryBuilder::PARAM_INT)))
54
-			->execute();
55
-	}
56
-
57
-	/**
58
-	 * @param int $olderThan
59
-	 * @param int $remember
60
-	 */
61
-	public function invalidateOld(int $olderThan, int $remember = IToken::DO_NOT_REMEMBER) {
62
-		/* @var $qb IQueryBuilder */
63
-		$qb = $this->db->getQueryBuilder();
64
-		$qb->delete('authtoken')
65
-			->where($qb->expr()->lt('last_activity', $qb->createNamedParameter($olderThan, IQueryBuilder::PARAM_INT)))
66
-			->andWhere($qb->expr()->eq('type', $qb->createNamedParameter(IToken::TEMPORARY_TOKEN, IQueryBuilder::PARAM_INT)))
67
-			->andWhere($qb->expr()->eq('remember', $qb->createNamedParameter($remember, IQueryBuilder::PARAM_INT)))
68
-			->andWhere($qb->expr()->eq('version', $qb->createNamedParameter(PublicKeyToken::VERSION, IQueryBuilder::PARAM_INT)))
69
-			->execute();
70
-	}
71
-
72
-	/**
73
-	 * Get the user UID for the given token
74
-	 *
75
-	 * @throws DoesNotExistException
76
-	 */
77
-	public function getToken(string $token): PublicKeyToken {
78
-		/* @var $qb IQueryBuilder */
79
-		$qb = $this->db->getQueryBuilder();
80
-		$result = $qb->select('*')
81
-			->from('authtoken')
82
-			->where($qb->expr()->eq('token', $qb->createNamedParameter($token)))
83
-			->andWhere($qb->expr()->eq('version', $qb->createNamedParameter(PublicKeyToken::VERSION, IQueryBuilder::PARAM_INT)))
84
-			->execute();
85
-
86
-		$data = $result->fetch();
87
-		$result->closeCursor();
88
-		if ($data === false) {
89
-			throw new DoesNotExistException('token does not exist');
90
-		}
91
-		return PublicKeyToken::fromRow($data);
92
-	}
93
-
94
-	/**
95
-	 * Get the token for $id
96
-	 *
97
-	 * @throws DoesNotExistException
98
-	 */
99
-	public function getTokenById(int $id): PublicKeyToken {
100
-		/* @var $qb IQueryBuilder */
101
-		$qb = $this->db->getQueryBuilder();
102
-		$result = $qb->select('*')
103
-			->from('authtoken')
104
-			->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
105
-			->andWhere($qb->expr()->eq('version', $qb->createNamedParameter(PublicKeyToken::VERSION, IQueryBuilder::PARAM_INT)))
106
-			->execute();
107
-
108
-		$data = $result->fetch();
109
-		$result->closeCursor();
110
-		if ($data === false) {
111
-			throw new DoesNotExistException('token does not exist');
112
-		}
113
-		return PublicKeyToken::fromRow($data);
114
-	}
115
-
116
-	/**
117
-	 * Get all tokens of a user
118
-	 *
119
-	 * The provider may limit the number of result rows in case of an abuse
120
-	 * where a high number of (session) tokens is generated
121
-	 *
122
-	 * @param string $uid
123
-	 * @return PublicKeyToken[]
124
-	 */
125
-	public function getTokenByUser(string $uid): array {
126
-		/* @var $qb IQueryBuilder */
127
-		$qb = $this->db->getQueryBuilder();
128
-		$qb->select('*')
129
-			->from('authtoken')
130
-			->where($qb->expr()->eq('uid', $qb->createNamedParameter($uid)))
131
-			->andWhere($qb->expr()->eq('version', $qb->createNamedParameter(PublicKeyToken::VERSION, IQueryBuilder::PARAM_INT)))
132
-			->setMaxResults(1000);
133
-		$result = $qb->execute();
134
-		$data = $result->fetchAll();
135
-		$result->closeCursor();
136
-
137
-		$entities = array_map(function ($row) {
138
-			return PublicKeyToken::fromRow($row);
139
-		}, $data);
140
-
141
-		return $entities;
142
-	}
143
-
144
-	public function deleteById(string $uid, int $id) {
145
-		/* @var $qb IQueryBuilder */
146
-		$qb = $this->db->getQueryBuilder();
147
-		$qb->delete('authtoken')
148
-			->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
149
-			->andWhere($qb->expr()->eq('uid', $qb->createNamedParameter($uid)))
150
-			->andWhere($qb->expr()->eq('version', $qb->createNamedParameter(PublicKeyToken::VERSION, IQueryBuilder::PARAM_INT)));
151
-		$qb->execute();
152
-	}
153
-
154
-	/**
155
-	 * delete all auth token which belong to a specific client if the client was deleted
156
-	 *
157
-	 * @param string $name
158
-	 */
159
-	public function deleteByName(string $name) {
160
-		$qb = $this->db->getQueryBuilder();
161
-		$qb->delete('authtoken')
162
-			->where($qb->expr()->eq('name', $qb->createNamedParameter($name), IQueryBuilder::PARAM_STR))
163
-			->andWhere($qb->expr()->eq('version', $qb->createNamedParameter(PublicKeyToken::VERSION, IQueryBuilder::PARAM_INT)));
164
-		$qb->execute();
165
-	}
166
-
167
-	public function deleteTempToken(PublicKeyToken $except) {
168
-		$qb = $this->db->getQueryBuilder();
169
-
170
-		$qb->delete('authtoken')
171
-			->where($qb->expr()->eq('uid', $qb->createNamedParameter($except->getUID())))
172
-			->andWhere($qb->expr()->eq('type', $qb->createNamedParameter(IToken::TEMPORARY_TOKEN)))
173
-			->andWhere($qb->expr()->neq('id', $qb->createNamedParameter($except->getId())))
174
-			->andWhere($qb->expr()->eq('version', $qb->createNamedParameter(PublicKeyToken::VERSION, IQueryBuilder::PARAM_INT)));
175
-
176
-		$qb->execute();
177
-	}
178
-
179
-	public function hasExpiredTokens(string $uid): bool {
180
-		$qb = $this->db->getQueryBuilder();
181
-		$qb->select('*')
182
-			->from('authtoken')
183
-			->where($qb->expr()->eq('uid', $qb->createNamedParameter($uid)))
184
-			->andWhere($qb->expr()->eq('password_invalid', $qb->createNamedParameter(true), IQueryBuilder::PARAM_BOOL))
185
-			->setMaxResults(1);
186
-
187
-		$cursor = $qb->execute();
188
-		$data = $cursor->fetchAll();
189
-		$cursor->closeCursor();
190
-
191
-		return count($data) === 1;
192
-	}
39
+    public function __construct(IDBConnection $db) {
40
+        parent::__construct($db, 'authtoken');
41
+    }
42
+
43
+    /**
44
+     * Invalidate (delete) a given token
45
+     *
46
+     * @param string $token
47
+     */
48
+    public function invalidate(string $token) {
49
+        /* @var $qb IQueryBuilder */
50
+        $qb = $this->db->getQueryBuilder();
51
+        $qb->delete('authtoken')
52
+            ->where($qb->expr()->eq('token', $qb->createNamedParameter($token)))
53
+            ->andWhere($qb->expr()->eq('version', $qb->createNamedParameter(PublicKeyToken::VERSION, IQueryBuilder::PARAM_INT)))
54
+            ->execute();
55
+    }
56
+
57
+    /**
58
+     * @param int $olderThan
59
+     * @param int $remember
60
+     */
61
+    public function invalidateOld(int $olderThan, int $remember = IToken::DO_NOT_REMEMBER) {
62
+        /* @var $qb IQueryBuilder */
63
+        $qb = $this->db->getQueryBuilder();
64
+        $qb->delete('authtoken')
65
+            ->where($qb->expr()->lt('last_activity', $qb->createNamedParameter($olderThan, IQueryBuilder::PARAM_INT)))
66
+            ->andWhere($qb->expr()->eq('type', $qb->createNamedParameter(IToken::TEMPORARY_TOKEN, IQueryBuilder::PARAM_INT)))
67
+            ->andWhere($qb->expr()->eq('remember', $qb->createNamedParameter($remember, IQueryBuilder::PARAM_INT)))
68
+            ->andWhere($qb->expr()->eq('version', $qb->createNamedParameter(PublicKeyToken::VERSION, IQueryBuilder::PARAM_INT)))
69
+            ->execute();
70
+    }
71
+
72
+    /**
73
+     * Get the user UID for the given token
74
+     *
75
+     * @throws DoesNotExistException
76
+     */
77
+    public function getToken(string $token): PublicKeyToken {
78
+        /* @var $qb IQueryBuilder */
79
+        $qb = $this->db->getQueryBuilder();
80
+        $result = $qb->select('*')
81
+            ->from('authtoken')
82
+            ->where($qb->expr()->eq('token', $qb->createNamedParameter($token)))
83
+            ->andWhere($qb->expr()->eq('version', $qb->createNamedParameter(PublicKeyToken::VERSION, IQueryBuilder::PARAM_INT)))
84
+            ->execute();
85
+
86
+        $data = $result->fetch();
87
+        $result->closeCursor();
88
+        if ($data === false) {
89
+            throw new DoesNotExistException('token does not exist');
90
+        }
91
+        return PublicKeyToken::fromRow($data);
92
+    }
93
+
94
+    /**
95
+     * Get the token for $id
96
+     *
97
+     * @throws DoesNotExistException
98
+     */
99
+    public function getTokenById(int $id): PublicKeyToken {
100
+        /* @var $qb IQueryBuilder */
101
+        $qb = $this->db->getQueryBuilder();
102
+        $result = $qb->select('*')
103
+            ->from('authtoken')
104
+            ->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
105
+            ->andWhere($qb->expr()->eq('version', $qb->createNamedParameter(PublicKeyToken::VERSION, IQueryBuilder::PARAM_INT)))
106
+            ->execute();
107
+
108
+        $data = $result->fetch();
109
+        $result->closeCursor();
110
+        if ($data === false) {
111
+            throw new DoesNotExistException('token does not exist');
112
+        }
113
+        return PublicKeyToken::fromRow($data);
114
+    }
115
+
116
+    /**
117
+     * Get all tokens of a user
118
+     *
119
+     * The provider may limit the number of result rows in case of an abuse
120
+     * where a high number of (session) tokens is generated
121
+     *
122
+     * @param string $uid
123
+     * @return PublicKeyToken[]
124
+     */
125
+    public function getTokenByUser(string $uid): array {
126
+        /* @var $qb IQueryBuilder */
127
+        $qb = $this->db->getQueryBuilder();
128
+        $qb->select('*')
129
+            ->from('authtoken')
130
+            ->where($qb->expr()->eq('uid', $qb->createNamedParameter($uid)))
131
+            ->andWhere($qb->expr()->eq('version', $qb->createNamedParameter(PublicKeyToken::VERSION, IQueryBuilder::PARAM_INT)))
132
+            ->setMaxResults(1000);
133
+        $result = $qb->execute();
134
+        $data = $result->fetchAll();
135
+        $result->closeCursor();
136
+
137
+        $entities = array_map(function ($row) {
138
+            return PublicKeyToken::fromRow($row);
139
+        }, $data);
140
+
141
+        return $entities;
142
+    }
143
+
144
+    public function deleteById(string $uid, int $id) {
145
+        /* @var $qb IQueryBuilder */
146
+        $qb = $this->db->getQueryBuilder();
147
+        $qb->delete('authtoken')
148
+            ->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
149
+            ->andWhere($qb->expr()->eq('uid', $qb->createNamedParameter($uid)))
150
+            ->andWhere($qb->expr()->eq('version', $qb->createNamedParameter(PublicKeyToken::VERSION, IQueryBuilder::PARAM_INT)));
151
+        $qb->execute();
152
+    }
153
+
154
+    /**
155
+     * delete all auth token which belong to a specific client if the client was deleted
156
+     *
157
+     * @param string $name
158
+     */
159
+    public function deleteByName(string $name) {
160
+        $qb = $this->db->getQueryBuilder();
161
+        $qb->delete('authtoken')
162
+            ->where($qb->expr()->eq('name', $qb->createNamedParameter($name), IQueryBuilder::PARAM_STR))
163
+            ->andWhere($qb->expr()->eq('version', $qb->createNamedParameter(PublicKeyToken::VERSION, IQueryBuilder::PARAM_INT)));
164
+        $qb->execute();
165
+    }
166
+
167
+    public function deleteTempToken(PublicKeyToken $except) {
168
+        $qb = $this->db->getQueryBuilder();
169
+
170
+        $qb->delete('authtoken')
171
+            ->where($qb->expr()->eq('uid', $qb->createNamedParameter($except->getUID())))
172
+            ->andWhere($qb->expr()->eq('type', $qb->createNamedParameter(IToken::TEMPORARY_TOKEN)))
173
+            ->andWhere($qb->expr()->neq('id', $qb->createNamedParameter($except->getId())))
174
+            ->andWhere($qb->expr()->eq('version', $qb->createNamedParameter(PublicKeyToken::VERSION, IQueryBuilder::PARAM_INT)));
175
+
176
+        $qb->execute();
177
+    }
178
+
179
+    public function hasExpiredTokens(string $uid): bool {
180
+        $qb = $this->db->getQueryBuilder();
181
+        $qb->select('*')
182
+            ->from('authtoken')
183
+            ->where($qb->expr()->eq('uid', $qb->createNamedParameter($uid)))
184
+            ->andWhere($qb->expr()->eq('password_invalid', $qb->createNamedParameter(true), IQueryBuilder::PARAM_BOOL))
185
+            ->setMaxResults(1);
186
+
187
+        $cursor = $qb->execute();
188
+        $data = $cursor->fetchAll();
189
+        $cursor->closeCursor();
190
+
191
+        return count($data) === 1;
192
+    }
193 193
 }
Please login to merge, or discard this patch.
lib/private/Authentication/Token/DefaultTokenMapper.php 1 patch
Indentation   +131 added lines, -131 removed lines patch added patch discarded remove patch
@@ -39,135 +39,135 @@
 block discarded – undo
39 39
  * @template-extends QBMapper<DefaultToken>
40 40
  */
41 41
 class DefaultTokenMapper extends QBMapper {
42
-	public function __construct(IDBConnection $db) {
43
-		parent::__construct($db, 'authtoken');
44
-	}
45
-
46
-	/**
47
-	 * Invalidate (delete) a given token
48
-	 *
49
-	 * @param string $token
50
-	 */
51
-	public function invalidate(string $token) {
52
-		/* @var $qb IQueryBuilder */
53
-		$qb = $this->db->getQueryBuilder();
54
-		$qb->delete('authtoken')
55
-			->where($qb->expr()->eq('token', $qb->createNamedParameter($token, IQueryBuilder::PARAM_STR)))
56
-			->andWhere($qb->expr()->eq('version', $qb->createNamedParameter(DefaultToken::VERSION, IQueryBuilder::PARAM_INT)))
57
-			->execute();
58
-	}
59
-
60
-	/**
61
-	 * @param int $olderThan
62
-	 * @param int $remember
63
-	 */
64
-	public function invalidateOld(int $olderThan, int $remember = IToken::DO_NOT_REMEMBER) {
65
-		/* @var $qb IQueryBuilder */
66
-		$qb = $this->db->getQueryBuilder();
67
-		$qb->delete('authtoken')
68
-			->where($qb->expr()->lt('last_activity', $qb->createNamedParameter($olderThan, IQueryBuilder::PARAM_INT)))
69
-			->andWhere($qb->expr()->eq('type', $qb->createNamedParameter(IToken::TEMPORARY_TOKEN, IQueryBuilder::PARAM_INT)))
70
-			->andWhere($qb->expr()->eq('remember', $qb->createNamedParameter($remember, IQueryBuilder::PARAM_INT)))
71
-			->andWhere($qb->expr()->eq('version', $qb->createNamedParameter(DefaultToken::VERSION, IQueryBuilder::PARAM_INT)))
72
-			->execute();
73
-	}
74
-
75
-	/**
76
-	 * Get the user UID for the given token
77
-	 *
78
-	 * @param string $token
79
-	 * @throws DoesNotExistException
80
-	 * @return DefaultToken
81
-	 */
82
-	public function getToken(string $token): DefaultToken {
83
-		/* @var $qb IQueryBuilder */
84
-		$qb = $this->db->getQueryBuilder();
85
-		$result = $qb->select('id', 'uid', 'login_name', 'password', 'name', 'token', 'type', 'remember', 'last_activity', 'last_check', 'scope', 'expires', 'version')
86
-			->from('authtoken')
87
-			->where($qb->expr()->eq('token', $qb->createNamedParameter($token)))
88
-			->andWhere($qb->expr()->eq('version', $qb->createNamedParameter(DefaultToken::VERSION, IQueryBuilder::PARAM_INT)))
89
-			->execute();
90
-
91
-		$data = $result->fetch();
92
-		$result->closeCursor();
93
-		if ($data === false) {
94
-			throw new DoesNotExistException('token does not exist');
95
-		}
96
-		return DefaultToken::fromRow($data);
97
-	}
98
-
99
-	/**
100
-	 * Get the token for $id
101
-	 *
102
-	 * @param int $id
103
-	 * @throws DoesNotExistException
104
-	 * @return DefaultToken
105
-	 */
106
-	public function getTokenById(int $id): DefaultToken {
107
-		/* @var $qb IQueryBuilder */
108
-		$qb = $this->db->getQueryBuilder();
109
-		$result = $qb->select('id', 'uid', 'login_name', 'password', 'name', 'token', 'type', 'remember', 'last_activity', 'last_check', 'scope', 'expires', 'version')
110
-			->from('authtoken')
111
-			->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
112
-			->andWhere($qb->expr()->eq('version', $qb->createNamedParameter(DefaultToken::VERSION, IQueryBuilder::PARAM_INT)))
113
-			->execute();
114
-
115
-		$data = $result->fetch();
116
-		$result->closeCursor();
117
-		if ($data === false) {
118
-			throw new DoesNotExistException('token does not exist');
119
-		}
120
-		return DefaultToken::fromRow($data);
121
-	}
122
-
123
-	/**
124
-	 * Get all tokens of a user
125
-	 *
126
-	 * The provider may limit the number of result rows in case of an abuse
127
-	 * where a high number of (session) tokens is generated
128
-	 *
129
-	 * @param string $uid
130
-	 * @return DefaultToken[]
131
-	 */
132
-	public function getTokenByUser(string $uid): array {
133
-		/* @var $qb IQueryBuilder */
134
-		$qb = $this->db->getQueryBuilder();
135
-		$qb->select('id', 'uid', 'login_name', 'password', 'name', 'token', 'type', 'remember', 'last_activity', 'last_check', 'scope', 'expires', 'version')
136
-			->from('authtoken')
137
-			->where($qb->expr()->eq('uid', $qb->createNamedParameter($uid)))
138
-			->andWhere($qb->expr()->eq('version', $qb->createNamedParameter(DefaultToken::VERSION, IQueryBuilder::PARAM_INT)))
139
-			->setMaxResults(1000);
140
-		$result = $qb->execute();
141
-		$data = $result->fetchAll();
142
-		$result->closeCursor();
143
-
144
-		$entities = array_map(function ($row) {
145
-			return DefaultToken::fromRow($row);
146
-		}, $data);
147
-
148
-		return $entities;
149
-	}
150
-
151
-	public function deleteById(string $uid, int $id) {
152
-		/* @var $qb IQueryBuilder */
153
-		$qb = $this->db->getQueryBuilder();
154
-		$qb->delete('authtoken')
155
-			->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
156
-			->andWhere($qb->expr()->eq('uid', $qb->createNamedParameter($uid)))
157
-			->andWhere($qb->expr()->eq('version', $qb->createNamedParameter(DefaultToken::VERSION, IQueryBuilder::PARAM_INT)));
158
-		$qb->execute();
159
-	}
160
-
161
-	/**
162
-	 * delete all auth token which belong to a specific client if the client was deleted
163
-	 *
164
-	 * @param string $name
165
-	 */
166
-	public function deleteByName(string $name) {
167
-		$qb = $this->db->getQueryBuilder();
168
-		$qb->delete('authtoken')
169
-			->where($qb->expr()->eq('name', $qb->createNamedParameter($name), IQueryBuilder::PARAM_STR))
170
-			->andWhere($qb->expr()->eq('version', $qb->createNamedParameter(DefaultToken::VERSION, IQueryBuilder::PARAM_INT)));
171
-		$qb->execute();
172
-	}
42
+    public function __construct(IDBConnection $db) {
43
+        parent::__construct($db, 'authtoken');
44
+    }
45
+
46
+    /**
47
+     * Invalidate (delete) a given token
48
+     *
49
+     * @param string $token
50
+     */
51
+    public function invalidate(string $token) {
52
+        /* @var $qb IQueryBuilder */
53
+        $qb = $this->db->getQueryBuilder();
54
+        $qb->delete('authtoken')
55
+            ->where($qb->expr()->eq('token', $qb->createNamedParameter($token, IQueryBuilder::PARAM_STR)))
56
+            ->andWhere($qb->expr()->eq('version', $qb->createNamedParameter(DefaultToken::VERSION, IQueryBuilder::PARAM_INT)))
57
+            ->execute();
58
+    }
59
+
60
+    /**
61
+     * @param int $olderThan
62
+     * @param int $remember
63
+     */
64
+    public function invalidateOld(int $olderThan, int $remember = IToken::DO_NOT_REMEMBER) {
65
+        /* @var $qb IQueryBuilder */
66
+        $qb = $this->db->getQueryBuilder();
67
+        $qb->delete('authtoken')
68
+            ->where($qb->expr()->lt('last_activity', $qb->createNamedParameter($olderThan, IQueryBuilder::PARAM_INT)))
69
+            ->andWhere($qb->expr()->eq('type', $qb->createNamedParameter(IToken::TEMPORARY_TOKEN, IQueryBuilder::PARAM_INT)))
70
+            ->andWhere($qb->expr()->eq('remember', $qb->createNamedParameter($remember, IQueryBuilder::PARAM_INT)))
71
+            ->andWhere($qb->expr()->eq('version', $qb->createNamedParameter(DefaultToken::VERSION, IQueryBuilder::PARAM_INT)))
72
+            ->execute();
73
+    }
74
+
75
+    /**
76
+     * Get the user UID for the given token
77
+     *
78
+     * @param string $token
79
+     * @throws DoesNotExistException
80
+     * @return DefaultToken
81
+     */
82
+    public function getToken(string $token): DefaultToken {
83
+        /* @var $qb IQueryBuilder */
84
+        $qb = $this->db->getQueryBuilder();
85
+        $result = $qb->select('id', 'uid', 'login_name', 'password', 'name', 'token', 'type', 'remember', 'last_activity', 'last_check', 'scope', 'expires', 'version')
86
+            ->from('authtoken')
87
+            ->where($qb->expr()->eq('token', $qb->createNamedParameter($token)))
88
+            ->andWhere($qb->expr()->eq('version', $qb->createNamedParameter(DefaultToken::VERSION, IQueryBuilder::PARAM_INT)))
89
+            ->execute();
90
+
91
+        $data = $result->fetch();
92
+        $result->closeCursor();
93
+        if ($data === false) {
94
+            throw new DoesNotExistException('token does not exist');
95
+        }
96
+        return DefaultToken::fromRow($data);
97
+    }
98
+
99
+    /**
100
+     * Get the token for $id
101
+     *
102
+     * @param int $id
103
+     * @throws DoesNotExistException
104
+     * @return DefaultToken
105
+     */
106
+    public function getTokenById(int $id): DefaultToken {
107
+        /* @var $qb IQueryBuilder */
108
+        $qb = $this->db->getQueryBuilder();
109
+        $result = $qb->select('id', 'uid', 'login_name', 'password', 'name', 'token', 'type', 'remember', 'last_activity', 'last_check', 'scope', 'expires', 'version')
110
+            ->from('authtoken')
111
+            ->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
112
+            ->andWhere($qb->expr()->eq('version', $qb->createNamedParameter(DefaultToken::VERSION, IQueryBuilder::PARAM_INT)))
113
+            ->execute();
114
+
115
+        $data = $result->fetch();
116
+        $result->closeCursor();
117
+        if ($data === false) {
118
+            throw new DoesNotExistException('token does not exist');
119
+        }
120
+        return DefaultToken::fromRow($data);
121
+    }
122
+
123
+    /**
124
+     * Get all tokens of a user
125
+     *
126
+     * The provider may limit the number of result rows in case of an abuse
127
+     * where a high number of (session) tokens is generated
128
+     *
129
+     * @param string $uid
130
+     * @return DefaultToken[]
131
+     */
132
+    public function getTokenByUser(string $uid): array {
133
+        /* @var $qb IQueryBuilder */
134
+        $qb = $this->db->getQueryBuilder();
135
+        $qb->select('id', 'uid', 'login_name', 'password', 'name', 'token', 'type', 'remember', 'last_activity', 'last_check', 'scope', 'expires', 'version')
136
+            ->from('authtoken')
137
+            ->where($qb->expr()->eq('uid', $qb->createNamedParameter($uid)))
138
+            ->andWhere($qb->expr()->eq('version', $qb->createNamedParameter(DefaultToken::VERSION, IQueryBuilder::PARAM_INT)))
139
+            ->setMaxResults(1000);
140
+        $result = $qb->execute();
141
+        $data = $result->fetchAll();
142
+        $result->closeCursor();
143
+
144
+        $entities = array_map(function ($row) {
145
+            return DefaultToken::fromRow($row);
146
+        }, $data);
147
+
148
+        return $entities;
149
+    }
150
+
151
+    public function deleteById(string $uid, int $id) {
152
+        /* @var $qb IQueryBuilder */
153
+        $qb = $this->db->getQueryBuilder();
154
+        $qb->delete('authtoken')
155
+            ->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
156
+            ->andWhere($qb->expr()->eq('uid', $qb->createNamedParameter($uid)))
157
+            ->andWhere($qb->expr()->eq('version', $qb->createNamedParameter(DefaultToken::VERSION, IQueryBuilder::PARAM_INT)));
158
+        $qb->execute();
159
+    }
160
+
161
+    /**
162
+     * delete all auth token which belong to a specific client if the client was deleted
163
+     *
164
+     * @param string $name
165
+     */
166
+    public function deleteByName(string $name) {
167
+        $qb = $this->db->getQueryBuilder();
168
+        $qb->delete('authtoken')
169
+            ->where($qb->expr()->eq('name', $qb->createNamedParameter($name), IQueryBuilder::PARAM_STR))
170
+            ->andWhere($qb->expr()->eq('version', $qb->createNamedParameter(DefaultToken::VERSION, IQueryBuilder::PARAM_INT)));
171
+        $qb->execute();
172
+    }
173 173
 }
Please login to merge, or discard this patch.