feat: Add API Key support for MCP authentication

- New db model: api_keys table with bcrypt-hashed keys
- New API: POST/GET/DELETE /v3/access/api-keys endpoints
- MCP _resolve_token: validates API keys, resolves to 5-min JWT
- API keys inherit the creating user's RBAC permissions
- MCP auth supports both JWT (24h) and API key (permanent) tokens
This commit is contained in:
YueGuobin committed 2026-06-11 22:54:03 +08:00
1 parent 8e9afbcf92
commit a79bc7dd20
8 files changed
+361 -6

No files matched your search

+1
View File
@@ -24,6 +24,7 @@ from .computes import Compute
from .images import Image
from .pools import Resource, ResourcePool
from .llm_model_configs import LLMModelConfig
from .api_keys import ApiKey
from .templates import (
Template,
CloudTemplate,
+33
View File
@@ -0,0 +1,33 @@
#
# Copyright (C) 2026 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from sqlalchemy import Column, String, Boolean, DateTime, ForeignKey, func
from .base import BaseTable, GUID
class ApiKey(BaseTable):
__tablename__ = "api_keys"
api_key_id = Column(GUID, primary_key=True)
user_id = Column(GUID, ForeignKey("users.user_id", ondelete="CASCADE"), nullable=False, index=True)
name = Column(String(128), nullable=False)
key_hash = Column(String(128), nullable=False)
key_prefix = Column(String(8), nullable=False)
last_used_at = Column(DateTime, nullable=True)
created_at = Column(DateTime, server_default=func.current_timestamp(), nullable=False)
revoked = Column(Boolean, default=False, nullable=False)