mirror of
https://github.com/jumpserver/jumpserver.git
synced 2025-05-08 08:06:25 +00:00
* feat: 添加 RBAC 应用模块 * feat: 添加 RBAC Model、API * feat: 添加 RBAC Model、API 2 * feat: 添加 RBAC Model、API 3 * feat: 添加 RBAC Model、API 4 * feat: RBAC * feat: RBAC * feat: RBAC * feat: RBAC * feat: RBAC * feat: RBAC 整理权限位 * feat: RBAC 整理权限位2 * feat: RBAC 整理权限位2 * feat: RBAC 整理权限位 * feat: RBAC 添加默认角色 * feat: RBAC 添加迁移文件;迁移用户角色->用户角色绑定 * feat: RBAC 添加迁移文件;迁移用户角色->用户角色绑定 * feat: RBAC 修改用户模块API * feat: RBAC 添加组织模块迁移文件 & 修改组织模块API * feat: RBAC 添加组织模块迁移文件 & 修改组织模块API * feat: RBAC 修改用户角色属性的使用 * feat: RBAC No.1 * xxx * perf: 暂存 * perf: ... * perf(rbac): 添加 perms 到 profile serializer 中 * stash * perf: 使用init * perf: 修改migrations * perf: rbac * stash * stash * pref: 修改rbac * stash it * stash: 先去修复其他bug * perf: 修改 role 添加 users * pref: 修改 RBAC Model * feat: 添加权限的 tree api * stash: 暂存一下 * stash: 暂存一下 * perf: 修改 model verbose name * feat: 添加model各种 verbose name * perf: 生成 migrations * perf: 优化权限位 * perf: 添加迁移脚本 * feat: 添加组织角色迁移 * perf: 添加迁移脚本 * stash * perf: 添加migrateion * perf: 暂存一下 * perf: 修改rbac * perf: stash it * fix: 迁移冲突 * fix: 迁移冲突 * perf: 暂存一下 * perf: 修改 rbac 逻辑 * stash: 暂存一下 * perf: 修改内置角色 * perf: 解决 root 组织的问题 * perf: stash it * perf: 优化 rbac * perf: 优化 rolebinding 处理 * perf: 完成用户离开组织的问题 * perf: 暂存一下 * perf: 修改翻译 * perf: 去掉了 IsSuperUser * perf: IsAppUser 去掉完成 * perf: 修改 connection token 的权限 * perf: 去掉导入的问题 * perf: perms define 格式,修改 app 用户 的全新啊 * perf: 修改 permission * perf: 去掉一些 org admin * perf: 去掉部分 org admin * perf: 再去掉点 org admin role * perf: 再去掉部分 org admin * perf: user 角色搜索 * perf: 去掉很多 js * perf: 添加权限位 * perf: 修改权限 * perf: 去掉一个 todo * merge: with dev * fix: 修复冲突 Co-authored-by: Bai <bugatti_it@163.com> Co-authored-by: Michael Bai <baijiangjie@gmail.com> Co-authored-by: ibuler <ibuler@qq.com>
89 lines
3.4 KiB
Python
89 lines
3.4 KiB
Python
from uuid import UUID
|
|
from urllib.parse import urlencode
|
|
|
|
from django.contrib.auth import login
|
|
from django.conf import settings
|
|
from django.http.response import HttpResponseRedirect
|
|
from rest_framework.decorators import action
|
|
from rest_framework.response import Response
|
|
from rest_framework.request import Request
|
|
from rest_framework.permissions import AllowAny
|
|
|
|
from common.utils.timezone import utc_now
|
|
from common.const.http import POST, GET
|
|
from common.drf.api import JMSGenericViewSet
|
|
from common.drf.serializers import EmptySerializer
|
|
from common.permissions import OnlySuperUser
|
|
from common.utils import reverse
|
|
from users.models import User
|
|
from ..serializers import SSOTokenSerializer
|
|
from ..models import SSOToken
|
|
from ..filters import AuthKeyQueryDeclaration
|
|
from ..mixins import AuthMixin
|
|
from ..errors import SSOAuthClosed
|
|
|
|
NEXT_URL = 'next'
|
|
AUTH_KEY = 'authkey'
|
|
|
|
|
|
class SSOViewSet(AuthMixin, JMSGenericViewSet):
|
|
queryset = SSOToken.objects.all()
|
|
serializer_classes = {
|
|
'login_url': SSOTokenSerializer,
|
|
'login': EmptySerializer
|
|
}
|
|
|
|
@action(methods=[POST], detail=False, permission_classes=[OnlySuperUser], url_path='login-url')
|
|
def login_url(self, request, *args, **kwargs):
|
|
if not settings.AUTH_SSO:
|
|
raise SSOAuthClosed()
|
|
|
|
serializer = self.get_serializer(data=request.data)
|
|
serializer.is_valid(raise_exception=True)
|
|
|
|
username = serializer.validated_data['username']
|
|
user = User.objects.get(username=username)
|
|
next_url = serializer.validated_data.get(NEXT_URL)
|
|
|
|
operator = request.user.username
|
|
# TODO `created_by` 和 `created_by` 可以通过 `ThreadLocal` 统一处理
|
|
token = SSOToken.objects.create(user=user, created_by=operator, updated_by=operator)
|
|
query = {
|
|
AUTH_KEY: token.authkey,
|
|
NEXT_URL: next_url or ''
|
|
}
|
|
login_url = '%s?%s' % (reverse('api-auth:sso-login', external=True), urlencode(query))
|
|
return Response(data={'login_url': login_url})
|
|
|
|
@action(methods=[GET], detail=False, filter_backends=[AuthKeyQueryDeclaration], permission_classes=[AllowAny])
|
|
def login(self, request: Request, *args, **kwargs):
|
|
"""
|
|
此接口违反了 `Restful` 的规范
|
|
`GET` 应该是安全的方法,但此接口是不安全的
|
|
"""
|
|
request.META['HTTP_X_JMS_LOGIN_TYPE'] = 'W'
|
|
authkey = request.query_params.get(AUTH_KEY)
|
|
next_url = request.query_params.get(NEXT_URL)
|
|
if not next_url or not next_url.startswith('/'):
|
|
next_url = reverse('index')
|
|
|
|
try:
|
|
authkey = UUID(authkey)
|
|
token = SSOToken.objects.get(authkey=authkey, expired=False)
|
|
# 先过期,只能访问这一次
|
|
token.expired = True
|
|
token.save()
|
|
except (ValueError, SSOToken.DoesNotExist):
|
|
self.send_auth_signal(success=False, reason='authkey_invalid')
|
|
return HttpResponseRedirect(next_url)
|
|
|
|
# 判断是否过期
|
|
if (utc_now().timestamp() - token.date_created.timestamp()) > settings.AUTH_SSO_AUTHKEY_TTL:
|
|
self.send_auth_signal(success=False, reason='authkey_timeout')
|
|
return HttpResponseRedirect(next_url)
|
|
|
|
user = token.user
|
|
login(self.request, user, 'authentication.backends.api.SSOAuthentication')
|
|
self.send_auth_signal(success=True, user=user)
|
|
return HttpResponseRedirect(next_url)
|