Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions ci/apiv2/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ def do_create_apitoken(extra_payload={}, **kwargs):
extra_payload = dict(extra_payload or {})
extra_payload.setdefault('startValid', now)
extra_payload.setdefault('endValid', now + 3600)
extra_payload.setdefault('tokenName', 'pytest-token')
return _do_create_obj_from_file(ApiToken, 'create_apitoken', extra_payload, **kwargs)


Expand All @@ -139,6 +140,7 @@ def create_apitoken_raw(test, auth, scopes):
'scopes': scopes,
'startValid': now,
'endValid': now + 3600,
'tokenName': 'pytest-token',
},
'type': 'ApiToken',
},
Expand Down
4 changes: 2 additions & 2 deletions ci/phpunit/TestBase.php
Original file line number Diff line number Diff line change
Expand Up @@ -234,10 +234,10 @@ protected function createTask(TaskWrapper $taskWrapper, CrackerBinary $crackerBi
/**
* @throws Exception
*/
protected function createJwtApiKey(User $user, ?int $startValid = null, ?int $endValid = null, int $isRevoked = 0): JwtApiKey {
protected function createJwtApiKey(User $user, ?int $startValid = null, ?int $endValid = null, int $isRevoked = 0, string $tokenName = "test-token"): JwtApiKey {
$key = $this->createDatabaseObject(
Factory::getJwtApiKeyFactory(),
new JwtApiKey(null, $startValid ?? time(), $endValid ?? time() + 3600, $user->getId(), $isRevoked)
new JwtApiKey(null, $startValid ?? time(), $endValid ?? time() + 3600, $user->getId(), $isRevoked, $tokenName)
);
$this->assertTrue($key instanceof JwtApiKey);
return $key;
Expand Down
5 changes: 3 additions & 2 deletions ci/phpunit/inc/utils/JwtTokenUtilsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,20 @@ public function testCreateKeyCreatesValidKey(): void {
$start = time();
$end = $start + 3600;

$key = JwtTokenUtils::createKey($this->user->getId(), $start, $end);
$key = JwtTokenUtils::createKey($this->user->getId(), $start, $end, 'test-token');

$this->assertInstanceOf(JwtApiKey::class, $key);
$this->assertSame($start, $key->getStartValid());
$this->assertSame($end, $key->getEndValid());
$this->assertSame($this->user->getId(), $key->getUserId());
$this->assertSame('test-token', $key->getTokenName());
$this->assertNotNull($key->getId());
$this->registerDatabaseObject(Factory::getJwtApiKeyFactory(), $key);
}

public function testCreateKeyThrowsForInvalidUser(): void {
$this->expectException(HttpError::class);
JwtTokenUtils::createKey(-1, time(), time() + 3600);
JwtTokenUtils::createKey(-1, time(), time() + 3600, 'test-token');
}

public function testDeleteKeyDeletesExpiredKey(): void {
Expand Down
15 changes: 14 additions & 1 deletion src/dba/models/JwtApiKey.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@ class JwtApiKey extends AbstractModel {
private ?int $endValid;
private ?int $userId;
private ?int $isRevoked;
private ?string $tokenName;

function __construct(?int $jwtApiKeyId, ?int $startValid, ?int $endValid, ?int $userId, ?int $isRevoked) {
function __construct(?int $jwtApiKeyId, ?int $startValid, ?int $endValid, ?int $userId, ?int $isRevoked, ?string $tokenName) {
$this->jwtApiKeyId = $jwtApiKeyId;
$this->startValid = $startValid;
$this->endValid = $endValid;
$this->userId = $userId;
$this->isRevoked = $isRevoked;
$this->tokenName = $tokenName;
}

function getKeyValueDict(): array {
Expand All @@ -26,6 +28,7 @@ function getKeyValueDict(): array {
$dict['endValid'] = $this->endValid;
$dict['userId'] = $this->userId;
$dict['isRevoked'] = $this->isRevoked;
$dict['tokenName'] = $this->tokenName;

return $dict;
}
Expand All @@ -37,6 +40,7 @@ static function getFeatures(): array {
$dict['endValid'] = ['read_only' => True, "type" => "int64", "subtype" => "unset", "choices" => "unset", "null" => False, "pk" => False, "protected" => False, "private" => False, "alias" => "endValid", "public" => False, "dba_mapping" => False];
$dict['userId'] = ['read_only' => True, "type" => "int", "subtype" => "unset", "choices" => "unset", "null" => True, "pk" => False, "protected" => False, "private" => False, "alias" => "userId", "public" => False, "dba_mapping" => False];
$dict['isRevoked'] = ['read_only' => False, "type" => "bool", "subtype" => "unset", "choices" => "unset", "null" => True, "pk" => False, "protected" => False, "private" => False, "alias" => "isRevoked", "public" => False, "dba_mapping" => False];
$dict['tokenName'] = ['read_only' => False, "type" => "str(100)", "subtype" => "unset", "choices" => "unset", "null" => False, "pk" => False, "protected" => False, "private" => False, "alias" => "tokenName", "public" => False, "dba_mapping" => False];

return $dict;
}
Expand Down Expand Up @@ -97,11 +101,20 @@ function setIsRevoked(?int $isRevoked): void {
$this->isRevoked = $isRevoked;
}

function getTokenName(): ?string {
return $this->tokenName;
}

function setTokenName(?string $tokenName): void {
$this->tokenName = $tokenName;
}

const JWT_API_KEY_ID = "jwtApiKeyId";
const START_VALID = "startValid";
const END_VALID = "endValid";
const USER_ID = "userId";
const IS_REVOKED = "isRevoked";
const TOKEN_NAME = "tokenName";

const PERM_CREATE = "permJwtApiKeyCreate";
const PERM_READ = "permJwtApiKeyRead";
Expand Down
4 changes: 2 additions & 2 deletions src/dba/models/JwtApiKeyFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ function getCacheValidTime(): int {
* @return JwtApiKey
*/
function getNullObject(): JwtApiKey {
return new JwtApiKey(-1, null, null, null, null);
return new JwtApiKey(-1, null, null, null, null, null);
}

/**
Expand All @@ -45,6 +45,6 @@ function createObjectFromDict(array $dict): JwtApiKey {
$conv[strtolower($key)] = $val;
}
$dict = $conv;
return new JwtApiKey($dict['jwtapikeyid'], $dict['startvalid'], $dict['endvalid'], $dict['userid'], $dict['isrevoked']);
return new JwtApiKey($dict['jwtapikeyid'], $dict['startvalid'], $dict['endvalid'], $dict['userid'], $dict['isrevoked'], $dict['tokenname']);
}
}
1 change: 1 addition & 0 deletions src/dba/models/generator.php
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,7 @@
['name' => 'endValid', 'read_only' => True, 'type' => 'int64'],
['name' => 'userId', 'read_only' => True, 'null' => True, 'type' => 'int', 'relation' => 'User'],
['name' => 'isRevoked', 'read_only' => False, 'null' => True, 'type' => 'bool'],
['name' => 'tokenName', 'read_only' => False, 'type' => 'str(100)'],
],
];
$CONF['LogEntry'] = [
Expand Down
3 changes: 2 additions & 1 deletion src/inc/apiv2/model/ApiTokenAPI.php
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,8 @@ protected function createObject(array $data): int {
$secret = StartupConfig::getInstance()->getPepper(0);
$iat = $data[JwtApiKey::START_VALID];
$expires = $data[JwtApiKey::END_VALID];
$token = JwtTokenUtils::createKey($this->getCurrentUser()->getId(), $iat, $expires);
$tokenName = $data[JwtApiKey::TOKEN_NAME];
$token = JwtTokenUtils::createKey($this->getCurrentUser()->getId(), $iat, $expires, $tokenName);
$jti = $token->getId();

$payload = [
Expand Down
5 changes: 3 additions & 2 deletions src/inc/utils/JwtTokenUtils.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,18 @@ class JwtTokenUtils {
* @param int $userId
* @param int $startValid
* @param int $endValid
* @param string $tokenName
* @return JwtApiKey
* @throws HttpError
* @throws Exception
*/
public static function createKey(int $userId, int $startValid, int $endValid): JwtApiKey {
public static function createKey(int $userId, int $startValid, int $endValid, string $tokenName): JwtApiKey {
$user = Factory::getUserFactory()->get($userId);
if ($user == null) {
throw new HttpError("Invalid user ID");
}

$key = new JwtApiKey(null, $startValid, $endValid, $userId, 0);
$key = new JwtApiKey(null, $startValid, $endValid, $userId, 0, $tokenName);
Factory::getJwtApiKeyFactory()->save($key);
return $key;
}
Expand Down
2 changes: 2 additions & 0 deletions src/migrations/mysql/20260810120000_jwtapikey-tokenname.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- Add tokenName column to JwtApiKey to allow naming individual API tokens.
ALTER TABLE `JwtApiKey` ADD COLUMN `tokenName` VARCHAR(100) NOT NULL DEFAULT '';
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- Add tokenName column to jwtapikey to allow naming individual API tokens.
ALTER TABLE jwtapikey ADD COLUMN tokenname VARCHAR(100) NOT NULL DEFAULT '';