Skip to content

Commit

Permalink
feat: rename user_token to user_apikeys
Browse files Browse the repository at this point in the history
  • Loading branch information
astappiev committed Aug 9, 2024
1 parent 8c08812 commit 38a60e8
Show file tree
Hide file tree
Showing 10 changed files with 69 additions and 59 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public class ApiKeyAuthMechanism implements HttpAuthenticationMechanism {
public static final AsciiString APIKEY_HEADER = AsciiString.cached("Api-Key");
public static final AsciiString AUTHORIZATION_HEADER = AsciiString.cached("Authorization");
public static final int AUTHORIZATION_HEADER_PREFIX_LENGTH = JWTAuthMechanism.BEARER.length() + 1;
public static final int AUTHORIZATION_HEADER_LENGTH = AUTHORIZATION_HEADER_PREFIX_LENGTH + ApiKey.length;
public static final int AUTHORIZATION_HEADER_LENGTH = AUTHORIZATION_HEADER_PREFIX_LENGTH + ApiKey.LENGTH;

@Override
public Uni<SecurityIdentity> authenticate(RoutingContext context, IdentityProviderManager identityProviderManager) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public Class<ApiKeyAuthenticationRequest> getRequestType() {
@Override
@WithSession
public Uni<SecurityIdentity> authenticate(ApiKeyAuthenticationRequest request, AuthenticationRequestContext authenticationRequestContext) {
return ApiKey.findByApiKey(request.getValue())
return ApiKey.findByApikey(request.getValue())
.onItem().ifNotNull()
.transform(key -> QuarkusSecurityIdentity.builder()
.setPrincipal(key.user)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import com.fasterxml.jackson.annotation.JsonIgnore;

import de.l3s.interweb.server.PanacheUtils;
import de.l3s.interweb.server.features.user.Token;
import de.l3s.interweb.server.features.user.ApiKey;

@Entity
@Cacheable
Expand All @@ -36,7 +36,7 @@ public class Chat extends PanacheEntityBase {
@NotNull
@JsonIgnore
@ManyToOne(optional = false, fetch = FetchType.LAZY)
public Token token;
public ApiKey apikey;

@Size(max = 32)
public String user;
Expand Down Expand Up @@ -89,9 +89,9 @@ public Uni<Integer> updateTitleAndUsage() {
return update("title = ?1, usedTokens = ?2, estimatedCost = ?3 where id = ?4", title, usedTokens, estimatedCost, id);
}

public static Uni<List<Chat>> listByUser(Token token, String user, String order, int page, int perPage) {
String query = "token.id = :id AND usedTokens != 0";
Parameters params = Parameters.with("id", token.id);
public static Uni<List<Chat>> listByUser(ApiKey apikey, String user, String order, int page, int perPage) {
String query = "apikey.id = :id AND usedTokens != 0";
Parameters params = Parameters.with("id", apikey.id);
Sort sort = PanacheUtils.createSort(order);

if (user != null) {
Expand All @@ -106,7 +106,7 @@ public static Uni<List<Chat>> listByUser(Token token, String user, String order,
return find(query, sort, params).page(page - 1, perPage).list();
}

public static Uni<Chat> findById(Token token, UUID id) {
return find("token.id = ?1 AND id = ?2", token.id, id).firstResult();
public static Uni<Chat> findById(ApiKey apikey, UUID id) {
return find("apikey.id = ?1 AND id = ?2", apikey.id, id).firstResult();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ public class ChatResource {
@POST
@Path("/completions")
public Uni<CompletionsResults> completions(@Valid CompletionsQuery query) {
Token token = securityIdentity.getCredential(Token.class);
ApiKey apikey = securityIdentity.getCredential(ApiKey.class);

return getOrCreateChat(query, token).flatMap(chat -> {
return getOrCreateChat(query, apikey).flatMap(chat -> {
// noinspection CodeBlock2Expr
return chatService.completions(query).call(results -> {
results.setChatId(chat.id);
Expand All @@ -60,13 +60,13 @@ public Uni<CompletionsResults> completions(@Valid CompletionsQuery query) {
});
}

private Uni<Chat> getOrCreateChat(CompletionsQuery query, Token token) {
private Uni<Chat> getOrCreateChat(CompletionsQuery query, ApiKey apikey) {
if (query.getId() != null) {
return Chat.findById(token, query.getId()).call(chat -> Mutiny.fetch(chat.getMessages()));
return Chat.findById(apikey, query.getId()).call(chat -> Mutiny.fetch(chat.getMessages()));
}

Chat chat = new Chat();
chat.token = token;
chat.apikey = apikey;
chat.model = query.getModel();
chat.user = query.getUser();
return Panache.withTransaction(chat::persist);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ public Uni<List<Chat>> chats(
@QueryParam("page") @DefaultValue("1") Integer page,
@QueryParam("perPage") @DefaultValue("20") Integer perPage
) {
Token token = securityIdentity.getCredential(Token.class);
ApiKey apikey = securityIdentity.getCredential(ApiKey.class);

return Chat.listByUser(token, user, order, page, perPage)
return Chat.listByUser(apikey, user, order, page, perPage)
.call(chats -> Multi.createFrom().iterable(chats)
.filter(chat -> chat.title == null)
.map(ChatsResource::createChatTitle)
Expand All @@ -62,9 +62,9 @@ private static Uni<List<ChatMessage>> createChatTitle(Chat chat) {
@GET
@Path("{uuid}")
public Uni<Conversation> chat(@PathParam("uuid") UUID id) {
Token token = securityIdentity.getCredential(Token.class);
ApiKey apikey = securityIdentity.getCredential(ApiKey.class);

return Chat.findById(token, id).call(chat -> Mutiny.fetch(chat.getMessages())).map(chat -> {
return Chat.findById(apikey, id).call(chat -> Mutiny.fetch(chat.getMessages())).map(chat -> {
Conversation conversation = new Conversation();
conversation.setId(chat.id);
conversation.setTitle(chat.title);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package de.l3s.interweb.server.features.user;

import java.time.Instant;
import java.util.List;

import jakarta.persistence.*;
Expand All @@ -10,16 +11,17 @@
import io.quarkus.hibernate.reactive.panache.PanacheEntityBase;
import io.quarkus.security.credential.Credential;
import io.smallrye.mutiny.Uni;
import org.hibernate.annotations.CreationTimestamp;

import com.fasterxml.jackson.annotation.JsonIgnore;

import de.l3s.interweb.core.util.StringUtils;

@Entity
@Cacheable
@Table(name = "user_token")
public class Token extends PanacheEntityBase implements Credential {
public static final int TOKEN_LENGTH = 64;
@Table(name = "user_apikey")
public class ApiKey extends PanacheEntityBase implements Credential {
public static final int LENGTH = 64;

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Expand All @@ -41,28 +43,31 @@ public class Token extends PanacheEntityBase implements Credential {

@NotEmpty
@NotNull
@Column(unique = true, length = TOKEN_LENGTH)
@Column(unique = true, length = LENGTH)
public String apikey;

public Token() {
@CreationTimestamp
public Instant created;

public ApiKey() {
// required for Panache
}

public static Token generate() {
Token token = new Token();
token.apikey = StringUtils.randomAlphanumeric(TOKEN_LENGTH);
return token;
public static ApiKey generate() {
ApiKey apikey = new ApiKey();
apikey.apikey = StringUtils.randomAlphanumeric(LENGTH);
return apikey;
}

public static Uni<List<Token>> findByUser(User user) {
public static Uni<List<ApiKey>> findByUser(User user) {
return list("user.id", user.id);
}

public static Uni<Token> findById(Object id, User user) {
public static Uni<ApiKey> findById(Object id, User user) {
return find("id = ?1 and user.id = ?2", id, user.id).firstResult();
}

public static Uni<Token> findByApiKey(String apikey) {
public static Uni<ApiKey> findByApikey(String apikey) {
return find("apikey", apikey).firstResult();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,43 +21,43 @@
import de.l3s.interweb.server.Roles;

@Tag(name = "API Keys", description = "Manage application access")
@Path("/tokens")
@Path("/api_keys")
@Authenticated
public class TokenResource {
public class ApiKeysResource {

@Context
SecurityIdentity securityIdentity;

@GET
@RolesAllowed({Roles.USER})
@Operation(summary = "List all tokens", description = "Use this method to list all tokens")
public Uni<List<Token>> tokens() {
return Token.findByUser((User) securityIdentity.getPrincipal());
@RolesAllowed({Roles.USER, Roles.ADMIN})
@Operation(summary = "List all api keys", description = "Use this method to list all api keys")
public Uni<List<ApiKey>> list() {
return ApiKey.findByUser((User) securityIdentity.getPrincipal());
}

@POST
@WithTransaction
@RolesAllowed({Roles.USER})
@Operation(summary = "Create a new token", description = "Use this method to create a new token")
public Uni<Token> newToken(@Valid CreateToken model) {
Token token = Token.generate();
token.name = model.name;
token.url = model.url;
token.description = model.description;
token.user = (User) securityIdentity.getPrincipal();
return token.persist();
@RolesAllowed({Roles.USER, Roles.ADMIN})
@Operation(summary = "Create a new api key", description = "Use this method to create a new api key")
public Uni<ApiKey> create(@Valid CreateToken model) {
ApiKey apikey = ApiKey.generate();
apikey.name = model.name;
apikey.url = model.url;
apikey.description = model.description;
apikey.user = (User) securityIdentity.getPrincipal();
return apikey.persist();
}

@DELETE
@WithTransaction
@Operation(summary = "Delete a token", description = "Use this method to delete a token. ")
public Uni<Void> deleteToken(@QueryParam("id") Long tokenId, @QueryParam("token") String token) {
Uni<Token> item;
@Operation(summary = "Delete an api key", description = "Use this method to delete an api key. ")
public Uni<Void> delete(@QueryParam("id") Long id, @QueryParam("apikey") String apikey) {
Uni<ApiKey> item;
User user = (User) securityIdentity.getPrincipal();
if (tokenId != null && user != null) {
item = Token.findById(tokenId, user);
if (id != null && user != null) {
item = ApiKey.findById(id, user);
} else {
item = Token.findByApiKey(token);
item = ApiKey.findByApikey(apikey);
}

return item.onItem().ifNotNull().call(PanacheEntityBase::delete).replaceWithVoid();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ CREATE TABLE IF NOT EXISTS `chat`
`title` VARCHAR(512) DEFAULT NULL,
`used_tokens` int(11) NOT NULL DEFAULT 0,
`estimated_cost` DOUBLE NOT NULL DEFAULT 0,
`created` DATETIME DEFAULT NULL,
`created` DATETIME DEFAULT NOW(),
KEY `index_chat_user` (`user`),
CONSTRAINT `fk_chat_token` FOREIGN KEY (`token_id`) REFERENCES `user_token` (`id`) ON DELETE SET NULL ON UPDATE CASCADE
);
Expand All @@ -18,7 +18,7 @@ CREATE TABLE IF NOT EXISTS `chat_message`
`chat_id` UUID NOT NULL,
`role` TINYINT UNSIGNED NOT NULL,
`content` TEXT NOT NULL,
`created` DATETIME DEFAULT NULL,
`created` DATETIME DEFAULT NOW(),
CONSTRAINT `fk_chat_message_chat` FOREIGN KEY (`chat_id`) REFERENCES `chat` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
ALTER TABLE `user_token` RENAME TO `user_apikey`;

ALTER TABLE `user_apikey` ADD `created` DATETIME DEFAULT NOW() AFTER `apikey`;
ALTER TABLE `chat` RENAME COLUMN `token_id` TO `apikey_id`;
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import java.util.List;
import java.util.Map;

import de.l3s.interweb.server.features.user.ApiKey;

import io.quarkus.panache.mock.PanacheMock;
import io.quarkus.test.InjectMock;
import io.quarkus.test.junit.QuarkusTest;
Expand All @@ -22,21 +24,20 @@
import de.l3s.interweb.core.search.SearchQuery;
import de.l3s.interweb.core.search.SearchResults;
import de.l3s.interweb.server.features.search.SearchService;
import de.l3s.interweb.server.features.user.Token;
import de.l3s.interweb.server.features.user.User;

@QuarkusTest
class RequestTokenAuthTest {
class RequestApiKeyAuthTest {

@InjectMock
SearchService searchService;

@BeforeEach
public void setup() {
PanacheMock.mock(Token.class);
Token testToken = Mockito.mock(Token.class);
Mockito.when(testToken.user).thenReturn(Mockito.mock(User.class));
Mockito.when(Token.findByApiKey("testkey")).thenReturn(Uni.createFrom().item(testToken));
PanacheMock.mock(ApiKey.class);
ApiKey testKey = Mockito.mock(ApiKey.class);
Mockito.when(testKey.user).thenReturn(Mockito.mock(User.class));
Mockito.when(ApiKey.findByApikey("testkey")).thenReturn(Uni.createFrom().item(testKey));

SearchQuery searchQuery = new SearchQuery();
searchQuery.setQuery("hello world");
Expand Down

0 comments on commit 38a60e8

Please sign in to comment.