### 2021/05/19 -- 更新:
+ 添加了企业微信支持,修改pwdselfservice/local_settings.py中的SCAN_CODE_TYPE = 'DING'或SCAN_CODE_TYPE = 'WEWORK',区分使用哪个应用扫码验证 + 添加Reids缓存Token支持,如果不配置Redis则使用MemoryStorage缓存到内存中
This commit is contained in:
parent
00d1d9a03c
commit
89b1c0de46
|
@ -2,6 +2,7 @@
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
from utils.ad_ops import AdOps
|
from utils.ad_ops import AdOps
|
||||||
|
from django_redis import get_redis_connection
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'pwdselfservice.settings')
|
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'pwdselfservice.settings')
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
from django_redis import get_redis_connection
|
||||||
|
|
||||||
|
from utils.storage.memorystorage import MemoryStorage
|
||||||
|
from utils.storage.kvstorage import KvStorage
|
||||||
|
|
||||||
|
try:
|
||||||
|
redis_conn = get_redis_connection()
|
||||||
|
cache_storage = KvStorage(redis_conn)
|
||||||
|
cache_storage.set('redis_connection', str(datetime.datetime))
|
||||||
|
cache_storage.get('redis_connection')
|
||||||
|
print("Redis连接成功,set/get测试通过,Token缓存将使用Redis处理")
|
||||||
|
except Exception as e:
|
||||||
|
cache_storage = MemoryStorage()
|
||||||
|
print("Redis无法连接,Token缓存将使用MemoryStorage处理")
|
||||||
|
print("如果确定需要使用Redis作为缓存,请排查Redis配置")
|
||||||
|
print("Exception: {}".format(e))
|
|
@ -2,7 +2,7 @@
|
||||||
# AD主机,可以是IP或主机域名,例如可以是: abc.com或172.16.122.1
|
# AD主机,可以是IP或主机域名,例如可以是: abc.com或172.16.122.1
|
||||||
AD_HOST = r'修改成自己的'
|
AD_HOST = r'修改成自己的'
|
||||||
|
|
||||||
# AD域控的DOMAIN名,例如:abc、abc.com
|
# AD域控的DOMAIN名,例如:abc
|
||||||
AD_DOMAIN = r'修改成自己的'
|
AD_DOMAIN = r'修改成自己的'
|
||||||
|
|
||||||
# 用于登录AD做用户信息处理的账号,需要有修改用户账号密码或信息的权限。
|
# 用于登录AD做用户信息处理的账号,需要有修改用户账号密码或信息的权限。
|
||||||
|
@ -20,9 +20,12 @@ AD_USE_SSL = True
|
||||||
# 连接的端口,如果启用SSL默认是636,否则就是389
|
# 连接的端口,如果启用SSL默认是636,否则就是389
|
||||||
AD_CONN_PORT = 636
|
AD_CONN_PORT = 636
|
||||||
|
|
||||||
|
# 扫码验证的类型
|
||||||
|
# 钉钉 / 企业微信,自行修改
|
||||||
|
# 值是:DING / WEWORK
|
||||||
|
SCAN_CODE_TYPE = 'DING'
|
||||||
|
|
||||||
# ########## 钉钉
|
# ########## 钉钉 《如果不使用钉钉扫码,可不用配置》##########
|
||||||
# 钉钉配置
|
|
||||||
# 钉钉接口主地址,不可修改
|
# 钉钉接口主地址,不可修改
|
||||||
DING_URL = r'https://oapi.dingtalk.com'
|
DING_URL = r'https://oapi.dingtalk.com'
|
||||||
|
|
||||||
|
@ -34,11 +37,26 @@ DING_AGENT_ID = r'修改为自己的'
|
||||||
DING_APP_KEY = r'修改为自己的'
|
DING_APP_KEY = r'修改为自己的'
|
||||||
DING_APP_SECRET = r'修改为自己的'
|
DING_APP_SECRET = r'修改为自己的'
|
||||||
|
|
||||||
# 移动应用接入 主要为了实现通过扫码拿到用户的unioid
|
# 移动应用接入 主要为了实现通过扫码拿到用户的unionid
|
||||||
DING_MO_APP_ID = r'修改为自己的'
|
DING_MO_APP_ID = r'修改为自己的'
|
||||||
DING_MO_APP_SECRET = r'修改为自己的'
|
DING_MO_APP_SECRET = r'修改为自己的'
|
||||||
|
|
||||||
# 执行:python3 ./resetpwd/utils/crypto.py 生成
|
|
||||||
|
# ####### 企业微信《如果不使用企业微信扫码,可不用配置》 ##########
|
||||||
|
# 企业微信的企业ID
|
||||||
|
WEWORK_CORP_ID = r'修改为自己的'
|
||||||
|
# 应用的AgentId
|
||||||
|
WEWORK_AGENT_ID = r'修改为自己的'
|
||||||
|
# 应用的Secret
|
||||||
|
WEWORK_AGNET_SECRET = r'修改为自己的'
|
||||||
|
|
||||||
|
# Redis配置
|
||||||
|
# redis的连接地址,redis://<Ip/Host>:<Port>/<数据库>
|
||||||
|
REDIS_LOCATION = r'redis://127.0.0.1:6379/1'
|
||||||
|
REDIS_PASSWORD = r'12345678'
|
||||||
|
|
||||||
|
# ##########################
|
||||||
|
# 执行:python3 ./utils/crypto.py 生成
|
||||||
# 可自行生成后替换
|
# 可自行生成后替换
|
||||||
CRYPTO_KEY = b'dp8U9y7NAhCD3MoNwPzPBhBtTZ1uI_WWSdpNs6wUDgs='
|
CRYPTO_KEY = b'dp8U9y7NAhCD3MoNwPzPBhBtTZ1uI_WWSdpNs6wUDgs='
|
||||||
|
|
||||||
|
@ -46,4 +64,5 @@ CRYPTO_KEY = b'dp8U9y7NAhCD3MoNwPzPBhBtTZ1uI_WWSdpNs6wUDgs='
|
||||||
TMPID_COOKIE_AGE = 300
|
TMPID_COOKIE_AGE = 300
|
||||||
|
|
||||||
# 主页域名,钉钉跳转等需要指定域名,格式:pwd.abc.com。
|
# 主页域名,钉钉跳转等需要指定域名,格式:pwd.abc.com。
|
||||||
|
# 如果是自定义安装,请修改成自己的域名
|
||||||
HOME_URL = 'PWD_SELF_SERVICE_DOMAIN'
|
HOME_URL = 'PWD_SELF_SERVICE_DOMAIN'
|
|
@ -1,16 +1,5 @@
|
||||||
"""
|
|
||||||
Django settings for pwdselfservice project.
|
|
||||||
|
|
||||||
Generated by 'django-admin startproject' using Django 2.1.8.
|
|
||||||
|
|
||||||
For more information on this file, see
|
|
||||||
https://docs.djangoproject.com/en/2.1/topics/settings/
|
|
||||||
|
|
||||||
For the full list of settings and their values, see
|
|
||||||
https://docs.djangoproject.com/en/2.1/ref/settings/
|
|
||||||
"""
|
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
from pwdselfservice.local_settings import REDIS_PASSWORD, REDIS_LOCATION
|
||||||
|
|
||||||
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
||||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
@ -127,17 +116,18 @@ TEMPLATES = [
|
||||||
|
|
||||||
WSGI_APPLICATION = 'pwdselfservice.wsgi.application'
|
WSGI_APPLICATION = 'pwdselfservice.wsgi.application'
|
||||||
|
|
||||||
|
CACHES = {
|
||||||
# Database
|
"default": {
|
||||||
# https://docs.djangoproject.com/en/2.1/ref/settings/#databases
|
"BACKEND": "django_redis.cache.RedisCache",
|
||||||
|
"LOCATION": REDIS_LOCATION,
|
||||||
# DATABASES = {
|
"OPTIONS": {
|
||||||
# 'default': {
|
"CLIENT_CLASS": "django_redis.client.DefaultClient",
|
||||||
# 'ENGINE': 'django.db.backends.sqlite3',
|
"PASSWORD": REDIS_PASSWORD,
|
||||||
# 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
|
"COMPRESSOR": "django_redis.compressors.zlib.ZlibCompressor",
|
||||||
# }
|
"IGNORE_EXCEPTIONS": True,
|
||||||
# }
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
AUTH_PASSWORD_VALIDATORS = [
|
AUTH_PASSWORD_VALIDATORS = [
|
||||||
{
|
{
|
||||||
|
@ -170,4 +160,3 @@ STATIC_URL = '/static/'
|
||||||
STATICFILES_DIRS = [
|
STATICFILES_DIRS = [
|
||||||
os.path.join(BASE_DIR, 'static'),
|
os.path.join(BASE_DIR, 'static'),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
84
readme.md
84
readme.md
|
@ -6,7 +6,7 @@
|
||||||
用户自行重置密码时如果通过手机号来进行钉钉与AD之间的验证就行不通了。
|
用户自行重置密码时如果通过手机号来进行钉钉与AD之间的验证就行不通了。
|
||||||
|
|
||||||
### 新版本逻辑:
|
### 新版本逻辑:
|
||||||
>用户扫码通过之后,通过临时授权码,提取用户的unionid,再通过unionid判断用户在本企业中是否存在。如果存在,提取用户钉钉账号的邮箱,通过邮箱转成账号,将账号拿到AD中进行比对来验证账号在AD中是否存在并账号状态是激活的。满足以上条件的账号就会视为可自行重置密码。
|
>用户扫码通过之后,通过临时授权码,提取用户的userid,再通过userid断用户在本企业中是否存在。如果存在,提取钉钉/企业微信用户的邮箱,通过邮箱转成账号,将账号拿到AD中进行比对来验证账号在AD中是否存在并账号状态是激活的。满足以上条件的账号就会视为可自行重置密码。
|
||||||
|
|
||||||
### 代码提交到--新分支:
|
### 代码提交到--新分支:
|
||||||
```
|
```
|
||||||
|
@ -29,6 +29,14 @@ AD必须使用SSL才能修改密码(这里被坑了N久...)
|
||||||
+ 重写了用户账号的格式兼容,现在用户账号可以兼容:username、DOMAIN\username、username@abc.com这三种格式。
|
+ 重写了用户账号的格式兼容,现在用户账号可以兼容:username、DOMAIN\username、username@abc.com这三种格式。
|
||||||
+ 优化了整体的代码逻辑,去掉一些冗余重复的代码。
|
+ 优化了整体的代码逻辑,去掉一些冗余重复的代码。
|
||||||
|
|
||||||
|
### 2021/05/19 -- 更新:
|
||||||
|
+ 添加了企业微信支持,修改pwdselfservice/local_settings.py中的SCAN_CODE_TYPE = 'DING'或SCAN_CODE_TYPE = 'WEWORK',区分使用哪个应用扫码验证
|
||||||
|
+ 添加Reids缓存Token支持,如果不配置Redis则使用MemoryStorage缓存到内存中
|
||||||
|
|
||||||
|
Redis的安装和配置方法请自行百度,比较简单
|
||||||
|
> 切记Redis一定请配置密码,弱密码或没有密码的Redis如果不小心暴露到公网,极其容易导致机器被黑用来挖矿。
|
||||||
|
|
||||||
|
整体验证逻辑不变,如果需要使用其它字段关联到AD验证的,请自行修改代码。
|
||||||
|
|
||||||
## 线上环境需要的基础环境:
|
## 线上环境需要的基础环境:
|
||||||
+ Python 3.8.9 (可自行下载源码包放到项目目录下,使用一键安装)
|
+ Python 3.8.9 (可自行下载源码包放到项目目录下,使用一键安装)
|
||||||
|
@ -37,6 +45,7 @@ AD必须使用SSL才能修改密码(这里被坑了N久...)
|
||||||
|
|
||||||
## 截图
|
## 截图
|
||||||

|

|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
|
@ -48,7 +57,9 @@ AD必须使用SSL才能修改密码(这里被坑了N久...)
|
||||||
|
|
||||||
参考截图配置:
|
参考截图配置:
|
||||||

|

|
||||||
|
|
||||||

|

|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
#### 移动接入应用--登录权限:
|
#### 移动接入应用--登录权限:
|
||||||
|
@ -59,6 +70,19 @@ AD必须使用SSL才能修改密码(这里被坑了N久...)
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
|
## 企业微信必要条件:
|
||||||
|
* 创建应用,记录下企业的CorpId,应用的ID和Secret。
|
||||||
|
|
||||||
|
参考截图:
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
## 使用脚本自动部署:
|
## 使用脚本自动部署:
|
||||||
使用脚本自动快速部署,只适合Centos,其它发行版本的Linux请自行修改相关命令。
|
使用脚本自动快速部署,只适合Centos,其它发行版本的Linux请自行修改相关命令。
|
||||||
|
|
||||||
|
@ -72,7 +96,7 @@ AD必须使用SSL才能修改密码(这里被坑了N久...)
|
||||||
# AD主机,可以是IP或主机域名,例如可以是: abc.com或172.16.122.1
|
# AD主机,可以是IP或主机域名,例如可以是: abc.com或172.16.122.1
|
||||||
AD_HOST = r'修改成自己的'
|
AD_HOST = r'修改成自己的'
|
||||||
|
|
||||||
# AD域控的DOMAIN名,例如:abc、abc.com
|
# AD域控的DOMAIN名,例如:abc
|
||||||
AD_DOMAIN = r'修改成自己的'
|
AD_DOMAIN = r'修改成自己的'
|
||||||
|
|
||||||
# 用于登录AD做用户信息处理的账号,需要有修改用户账号密码或信息的权限。
|
# 用于登录AD做用户信息处理的账号,需要有修改用户账号密码或信息的权限。
|
||||||
|
@ -90,9 +114,12 @@ AD_USE_SSL = True
|
||||||
# 连接的端口,如果启用SSL默认是636,否则就是389
|
# 连接的端口,如果启用SSL默认是636,否则就是389
|
||||||
AD_CONN_PORT = 636
|
AD_CONN_PORT = 636
|
||||||
|
|
||||||
|
# 扫码验证的类型
|
||||||
|
# 钉钉 / 企业微信,自行修改
|
||||||
|
# 值是:DING / WEWORK
|
||||||
|
SCAN_CODE_TYPE = 'DING'
|
||||||
|
|
||||||
# ########## 钉钉
|
# ########## 钉钉 《如果不使用钉钉扫码,可不用配置》##########
|
||||||
# 钉钉配置
|
|
||||||
# 钉钉接口主地址,不可修改
|
# 钉钉接口主地址,不可修改
|
||||||
DING_URL = r'https://oapi.dingtalk.com'
|
DING_URL = r'https://oapi.dingtalk.com'
|
||||||
|
|
||||||
|
@ -104,11 +131,26 @@ DING_AGENT_ID = r'修改为自己的'
|
||||||
DING_APP_KEY = r'修改为自己的'
|
DING_APP_KEY = r'修改为自己的'
|
||||||
DING_APP_SECRET = r'修改为自己的'
|
DING_APP_SECRET = r'修改为自己的'
|
||||||
|
|
||||||
# 移动应用接入 主要为了实现通过扫码拿到用户的unioid
|
# 移动应用接入 主要为了实现通过扫码拿到用户的unionid
|
||||||
DING_MO_APP_ID = r'修改为自己的'
|
DING_MO_APP_ID = r'修改为自己的'
|
||||||
DING_MO_APP_SECRET = r'修改为自己的'
|
DING_MO_APP_SECRET = r'修改为自己的'
|
||||||
|
|
||||||
# 执行:python3 ./resetpwd/utils/crypto.py 生成
|
|
||||||
|
# ####### 企业微信《如果不使用企业微信扫码,可不用配置》 ##########
|
||||||
|
# 企业微信的企业ID
|
||||||
|
WEWORK_CORP_ID = r'修改为自己的'
|
||||||
|
# 应用的AgentId
|
||||||
|
WEWORK_AGENT_ID = r'修改为自己的'
|
||||||
|
# 应用的Secret
|
||||||
|
WEWORK_AGNET_SECRET = r'修改为自己的'
|
||||||
|
|
||||||
|
# Redis配置
|
||||||
|
# redis的连接地址,redis://<Ip/Host>:<Port>/<数据库>
|
||||||
|
REDIS_LOCATION = r'redis://127.0.0.1:6379/1'
|
||||||
|
REDIS_PASSWORD = r'12345678'
|
||||||
|
|
||||||
|
# ##########################
|
||||||
|
# 执行:python3 ./utils/crypto.py 生成
|
||||||
# 可自行生成后替换
|
# 可自行生成后替换
|
||||||
CRYPTO_KEY = b'dp8U9y7NAhCD3MoNwPzPBhBtTZ1uI_WWSdpNs6wUDgs='
|
CRYPTO_KEY = b'dp8U9y7NAhCD3MoNwPzPBhBtTZ1uI_WWSdpNs6wUDgs='
|
||||||
|
|
||||||
|
@ -116,6 +158,7 @@ CRYPTO_KEY = b'dp8U9y7NAhCD3MoNwPzPBhBtTZ1uI_WWSdpNs6wUDgs='
|
||||||
TMPID_COOKIE_AGE = 300
|
TMPID_COOKIE_AGE = 300
|
||||||
|
|
||||||
# 主页域名,钉钉跳转等需要指定域名,格式:pwd.abc.com。
|
# 主页域名,钉钉跳转等需要指定域名,格式:pwd.abc.com。
|
||||||
|
# 如果是自定义安装,请修改成自己的域名
|
||||||
HOME_URL = 'PWD_SELF_SERVICE_DOMAIN'
|
HOME_URL = 'PWD_SELF_SERVICE_DOMAIN'
|
||||||
````
|
````
|
||||||
### 执行部署脚本
|
### 执行部署脚本
|
||||||
|
@ -146,7 +189,7 @@ chmod +x auto-install.sh
|
||||||
# AD主机,可以是IP或主机域名,例如可以是: abc.com或172.16.122.1
|
# AD主机,可以是IP或主机域名,例如可以是: abc.com或172.16.122.1
|
||||||
AD_HOST = r'修改成自己的'
|
AD_HOST = r'修改成自己的'
|
||||||
|
|
||||||
# AD域控的DOMAIN名,例如:abc、abc.com
|
# AD域控的DOMAIN名,例如:abc
|
||||||
AD_DOMAIN = r'修改成自己的'
|
AD_DOMAIN = r'修改成自己的'
|
||||||
|
|
||||||
# 用于登录AD做用户信息处理的账号,需要有修改用户账号密码或信息的权限。
|
# 用于登录AD做用户信息处理的账号,需要有修改用户账号密码或信息的权限。
|
||||||
|
@ -164,9 +207,12 @@ AD_USE_SSL = True
|
||||||
# 连接的端口,如果启用SSL默认是636,否则就是389
|
# 连接的端口,如果启用SSL默认是636,否则就是389
|
||||||
AD_CONN_PORT = 636
|
AD_CONN_PORT = 636
|
||||||
|
|
||||||
|
# 扫码验证的类型
|
||||||
|
# 钉钉 / 企业微信,自行修改
|
||||||
|
# 值是:DING / WEWORK
|
||||||
|
SCAN_CODE_TYPE = 'DING'
|
||||||
|
|
||||||
# ########## 钉钉
|
# ########## 钉钉 《如果不使用钉钉扫码,可不用配置》##########
|
||||||
# 钉钉配置
|
|
||||||
# 钉钉接口主地址,不可修改
|
# 钉钉接口主地址,不可修改
|
||||||
DING_URL = r'https://oapi.dingtalk.com'
|
DING_URL = r'https://oapi.dingtalk.com'
|
||||||
|
|
||||||
|
@ -178,11 +224,26 @@ DING_AGENT_ID = r'修改为自己的'
|
||||||
DING_APP_KEY = r'修改为自己的'
|
DING_APP_KEY = r'修改为自己的'
|
||||||
DING_APP_SECRET = r'修改为自己的'
|
DING_APP_SECRET = r'修改为自己的'
|
||||||
|
|
||||||
# 移动应用接入 主要为了实现通过扫码拿到用户的unioid
|
# 移动应用接入 主要为了实现通过扫码拿到用户的unionid
|
||||||
DING_MO_APP_ID = r'修改为自己的'
|
DING_MO_APP_ID = r'修改为自己的'
|
||||||
DING_MO_APP_SECRET = r'修改为自己的'
|
DING_MO_APP_SECRET = r'修改为自己的'
|
||||||
|
|
||||||
# 执行:python3 ./resetpwd/utils/crypto.py 生成
|
|
||||||
|
# ####### 企业微信《如果不使用企业微信扫码,可不用配置》 ##########
|
||||||
|
# 企业微信的企业ID
|
||||||
|
WEWORK_CORP_ID = r'修改为自己的'
|
||||||
|
# 应用的AgentId
|
||||||
|
WEWORK_AGENT_ID = r'修改为自己的'
|
||||||
|
# 应用的Secret
|
||||||
|
WEWORK_AGNET_SECRET = r'修改为自己的'
|
||||||
|
|
||||||
|
# Redis配置
|
||||||
|
# redis的连接地址,redis://<Ip/Host>:<Port>/<数据库>
|
||||||
|
REDIS_LOCATION = r'redis://127.0.0.1:6379/1'
|
||||||
|
REDIS_PASSWORD = r'12345678'
|
||||||
|
|
||||||
|
# ##########################
|
||||||
|
# 执行:python3 ./utils/crypto.py 生成
|
||||||
# 可自行生成后替换
|
# 可自行生成后替换
|
||||||
CRYPTO_KEY = b'dp8U9y7NAhCD3MoNwPzPBhBtTZ1uI_WWSdpNs6wUDgs='
|
CRYPTO_KEY = b'dp8U9y7NAhCD3MoNwPzPBhBtTZ1uI_WWSdpNs6wUDgs='
|
||||||
|
|
||||||
|
@ -190,6 +251,7 @@ CRYPTO_KEY = b'dp8U9y7NAhCD3MoNwPzPBhBtTZ1uI_WWSdpNs6wUDgs='
|
||||||
TMPID_COOKIE_AGE = 300
|
TMPID_COOKIE_AGE = 300
|
||||||
|
|
||||||
# 主页域名,钉钉跳转等需要指定域名,格式:pwd.abc.com。
|
# 主页域名,钉钉跳转等需要指定域名,格式:pwd.abc.com。
|
||||||
|
# 如果是自定义安装,请修改成自己的域名
|
||||||
HOME_URL = 'PWD_SELF_SERVICE_DOMAIN'
|
HOME_URL = 'PWD_SELF_SERVICE_DOMAIN'
|
||||||
````
|
````
|
||||||
|
|
||||||
|
|
|
@ -2,5 +2,6 @@ Django==3.2
|
||||||
dingtalk-sdk==1.3.8
|
dingtalk-sdk==1.3.8
|
||||||
cryptography==3.4.7
|
cryptography==3.4.7
|
||||||
ldap3==2.9
|
ldap3==2.9
|
||||||
|
django-redis==4.12.1
|
||||||
requests
|
requests
|
||||||
uwsgi
|
uwsgi
|
|
@ -1,40 +1,51 @@
|
||||||
import logging
|
import logging
|
||||||
import sys
|
|
||||||
|
|
||||||
from django.http import *
|
from django.http import *
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
|
from pwdselfservice.local_settings import SCAN_CODE_TYPE, DING_MO_APP_ID, WEWORK_CORP_ID, WEWORK_AGENT_ID, HOME_URL, CRYPTO_KEY, TMPID_COOKIE_AGE
|
||||||
|
|
||||||
from utils.ad_ops import *
|
from utils.ad_ops import AdOps
|
||||||
from utils.crypto import Crypto
|
from utils.crypto import Crypto
|
||||||
from utils.dingding_ops import *
|
from utils.format_username import format2username, get_user_is_active
|
||||||
from utils.format_username import format2username
|
|
||||||
from .form import CheckForm
|
from .form import CheckForm
|
||||||
|
|
||||||
msg_template = 'messages.html'
|
msg_template = 'messages.html'
|
||||||
logger = logging.getLogger('django')
|
logger = logging.getLogger('django')
|
||||||
|
|
||||||
try:
|
|
||||||
ad_ops = AdOps()
|
class PARAMS(object):
|
||||||
except Exception as e:
|
if SCAN_CODE_TYPE == 'DING':
|
||||||
print(e)
|
app_id = DING_MO_APP_ID
|
||||||
sys.exit(1)
|
agent_id = None
|
||||||
try:
|
SCAN_APP = '钉钉'
|
||||||
ding_ops = DingDingOps()
|
from utils.dingding_ops import DingDingOps
|
||||||
except Exception as e:
|
ops = DingDingOps()
|
||||||
print(e)
|
elif SCAN_CODE_TYPE == 'WEWORK':
|
||||||
sys.exit(1)
|
app_id = WEWORK_CORP_ID
|
||||||
|
agent_id = WEWORK_AGENT_ID
|
||||||
|
SCAN_APP = '微信'
|
||||||
|
from utils.wework_ops import WeWorkOps
|
||||||
|
ops = WeWorkOps()
|
||||||
|
|
||||||
|
|
||||||
|
scan_params = PARAMS()
|
||||||
|
_ops = scan_params.ops
|
||||||
|
ad_ops = AdOps()
|
||||||
|
|
||||||
|
|
||||||
def index(request):
|
def index(request):
|
||||||
"""
|
"""
|
||||||
用户自行修改密码
|
用户自行修改密码/首页
|
||||||
:param request:
|
:param request:
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
home_url = '%s://%s' % (request.scheme, HOME_URL)
|
home_url = '%s://%s' % (request.scheme, HOME_URL)
|
||||||
app_id = DING_MO_APP_ID
|
app_id = scan_params.app_id
|
||||||
if request.method == 'GET':
|
agent_id = scan_params.agent_id
|
||||||
return render(request, 'index.html', locals())
|
if request.method == 'GET' and SCAN_CODE_TYPE == 'DING':
|
||||||
|
return render(request, 'ding_index.html', locals())
|
||||||
|
elif request.method == 'GET' and SCAN_CODE_TYPE == 'WEWORK':
|
||||||
|
return render(request, 'we_index.html', locals())
|
||||||
else:
|
else:
|
||||||
logger.error('[异常] 请求方法:%s,请求路径:%s' % (request.method, request.path))
|
logger.error('[异常] 请求方法:%s,请求路径:%s' % (request.method, request.path))
|
||||||
|
|
||||||
|
@ -68,8 +79,9 @@ def index(request):
|
||||||
return render(request, msg_template, context)
|
return render(request, msg_template, context)
|
||||||
|
|
||||||
# 514 66050是AD中账号被禁用的特定代码,这个可以在微软官网查到。
|
# 514 66050是AD中账号被禁用的特定代码,这个可以在微软官网查到。
|
||||||
# 可能不是太准确
|
# 可能不是太准确,如果使用者能确定还有其它状态码,可以自行在此处添加
|
||||||
if ad_ops.ad_get_user_status_by_account(username) == 514 or ad_ops.ad_get_user_status_by_account(username) == 66050:
|
account_code = ad_ops.ad_get_user_status_by_account(username)
|
||||||
|
if account_code == 514 or account_code == 66050:
|
||||||
context = {
|
context = {
|
||||||
'msg': "此账号状态为己禁用,请联系HR确认账号是否正确。",
|
'msg': "此账号状态为己禁用,请联系HR确认账号是否正确。",
|
||||||
'button_click': "window.location.href='%s'" % home_url,
|
'button_click': "window.location.href='%s'" % home_url,
|
||||||
|
@ -87,7 +99,7 @@ def index(request):
|
||||||
return render(request, msg_template, context)
|
return render(request, msg_template, context)
|
||||||
else:
|
else:
|
||||||
context = {
|
context = {
|
||||||
'msg': "密码未修改成功,原因:{}" .format(reset_result),
|
'msg': "密码未修改成功,原因:{}".format(reset_result),
|
||||||
'button_click': "window.history.back()",
|
'button_click': "window.history.back()",
|
||||||
'button_display': "返回"
|
'button_display': "返回"
|
||||||
}
|
}
|
||||||
|
@ -113,65 +125,57 @@ def callback_check(request):
|
||||||
logger.info('[成功] 请求方法:%s,请求路径:%s,CODE:%s' % (request.method, request.path, code))
|
logger.info('[成功] 请求方法:%s,请求路径:%s,CODE:%s' % (request.method, request.path, code))
|
||||||
else:
|
else:
|
||||||
logger.error('[异常] 请求方法:%s,请求路径:%s,未能拿到CODE。' % (request.method, request.path))
|
logger.error('[异常] 请求方法:%s,请求路径:%s,未能拿到CODE。' % (request.method, request.path))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
union_status, union_id = ding_ops.ding_get_union_id_by_code(code)
|
_status, user_id = _ops.get_user_id_by_code(code)
|
||||||
# 判断 union_id 在本企业钉钉中是否存在
|
# 判断 user_id 在本企业钉钉/微信中是否存在
|
||||||
if not union_status:
|
if not _status:
|
||||||
logger.error('[异常] 请求方法:%s,请求路径:%s,未能拿到union_id。' % (request.method, request.path))
|
|
||||||
context = {
|
context = {
|
||||||
'msg': '未能在企业钉钉中检索到用户信息,错误信息:{}' .format(union_id),
|
'msg': '获取钉钉userid失败,错误信息:{}'.format(user_id),
|
||||||
'button_click': "window.location.href='%s'" % home_url,
|
'button_click': "window.location.href='%s'" % home_url,
|
||||||
'button_display': "返回主页"
|
'button_display': "返回主页"
|
||||||
}
|
}
|
||||||
return render(request, msg_template, context)
|
return render(request, msg_template, context)
|
||||||
userid_status, user_result = ding_ops.ding_get_userid_by_union_id(union_id)
|
detail_status, user_info = _ops.get_user_detail_by_user_id(user_id)
|
||||||
if not userid_status:
|
|
||||||
context = {
|
|
||||||
'msg': '获取钉钉userid失败,错误信息:{}'.format(user_result),
|
|
||||||
'button_click': "window.location.href='%s'" % home_url,
|
|
||||||
'button_display': "返回主页"
|
|
||||||
}
|
|
||||||
return render(request, msg_template, context)
|
|
||||||
detail_status, ding_user_info = ding_ops.ding_get_userinfo_detail(user_result)
|
|
||||||
if not detail_status:
|
if not detail_status:
|
||||||
context = {
|
context = {
|
||||||
'msg': '获取钉钉用户信息失败,错误信息:{}'.format(ding_user_info),
|
'msg': '获取钉钉用户信息失败,错误信息:{}'.format(user_info),
|
||||||
'button_click': "window.location.href='%s'" % home_url,
|
'button_click': "window.location.href='%s'" % home_url,
|
||||||
'button_display': "返回主页"
|
'button_display': "返回主页"
|
||||||
}
|
}
|
||||||
return render(request, msg_template, context)
|
return render(request, msg_template, context)
|
||||||
# 钉钉中此账号是否可用
|
# 账号是否是激活的
|
||||||
if ding_user_info['active']:
|
if get_user_is_active(user_info):
|
||||||
crypto = Crypto(CRYPTO_KEY)
|
crypto = Crypto(CRYPTO_KEY)
|
||||||
# 对union_id进行加密,因为union_id基本上固定不变的,为了防止union_id泄露而导致重复使用,进行加密后再传回。
|
# 对user_id进行加密,因为user_id基本上固定不变的,为了防止user_id泄露而导致重复使用,进行加密后再传回。
|
||||||
union_id_cryto = crypto.encrypt(union_id)
|
_id_cryto = crypto.encrypt(user_id)
|
||||||
# 配置cookie,通过cookie把加密后的用户union_id传到重置密码页面,并重定向到重置密码页面。
|
# 配置cookie,通过cookie把加密后的用户user_id传到重置密码页面,并重定向到重置密码页面。
|
||||||
set_cookie = HttpResponseRedirect('resetPassword')
|
set_cookie = HttpResponseRedirect('resetPassword')
|
||||||
set_cookie.set_cookie('tmpid', union_id_cryto, expires=TMPID_COOKIE_AGE)
|
set_cookie.set_cookie('tmpid', _id_cryto, expires=TMPID_COOKIE_AGE)
|
||||||
return set_cookie
|
return set_cookie
|
||||||
else:
|
else:
|
||||||
context = {
|
context = {
|
||||||
'msg': '[%s]在钉钉中未激活或可能己离职' % format2username(ding_user_info['name']),
|
'msg': '[%s]在钉钉中未激活或可能己离职' % format2username(user_info.get('name')),
|
||||||
'button_click': "window.location.href='%s'" % home_url,
|
'button_click': "window.location.href='%s'" % home_url,
|
||||||
'button_display': "返回主页"
|
'button_display': "返回主页"
|
||||||
}
|
}
|
||||||
return render(request, msg_template, context)
|
return render(request, msg_template, context)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
context = {
|
context = {
|
||||||
'msg': "错误,钉钉临时Code己失效,请从主页重新扫码。",
|
'msg': "错误,临时授权码己失效,请从主页重新扫码验证。",
|
||||||
'button_click': "window.location.href='%s'" % home_url,
|
'button_click': "window.location.href='%s'" % home_url,
|
||||||
'button_display': "返回主页"
|
'button_display': "返回主页"
|
||||||
}
|
}
|
||||||
logger.error('[异常] :%s' % str(KeyError))
|
logger.error('[异常] :%s' % str(KeyError))
|
||||||
return render(request, msg_template, context)
|
return render(request, msg_template, context)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as callback_e:
|
||||||
context = {
|
context = {
|
||||||
'msg': "错误[%s],请与管理员联系." % str(e),
|
'msg': "错误[%s],请与管理员联系." % str(callback_e),
|
||||||
'button_click': "window.location.href='%s'" % home_url,
|
'button_click': "window.location.href='%s'" % home_url,
|
||||||
'button_display': "返回主页"
|
'button_display': "返回主页"
|
||||||
}
|
}
|
||||||
logger.error('[异常] :%s' % str(e))
|
logger.error('[异常] :%s' % str(callback_e))
|
||||||
return render(request, msg_template, context)
|
return render(request, msg_template, context)
|
||||||
|
|
||||||
|
|
||||||
|
@ -181,17 +185,17 @@ def reset_pwd_by_ding_callback(request):
|
||||||
:param request:
|
:param request:
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
global union_id_crypto
|
global tmpid_crypto
|
||||||
home_url = '%s://%s' % (request.scheme, HOME_URL)
|
home_url = '%s://%s' % (request.scheme, HOME_URL)
|
||||||
# 从cookie中提取union_id,并解密,然后对当前union_id的用户进行重置密码
|
# 从cookie中提取union_id,并解密,然后对当前union_id的用户进行重置密码
|
||||||
if request.method == 'GET':
|
if request.method == 'GET':
|
||||||
try:
|
try:
|
||||||
union_id_crypto = request.COOKIES.get('tmpid')
|
tmpid_crypto = request.COOKIES.get('tmpid')
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
union_id_crypto = None
|
tmpid_crypto = None
|
||||||
logger.error('[异常] :%s' % str(e))
|
logger.error('[异常] :%s' % str(e))
|
||||||
if not union_id_crypto:
|
if not tmpid_crypto:
|
||||||
logger.error('[异常] 请求方法:%s,请求路径:%s,未能拿到CODE或CODE己超时。' % (request.method, request.path))
|
logger.error('[异常] 请求方法:%s,请求路径:%s,未能拿到TmpID或会话己超时。' % (request.method, request.path))
|
||||||
context = {
|
context = {
|
||||||
'msg': "会话己超时,请重新扫码验证用户信息。",
|
'msg': "会话己超时,请重新扫码验证用户信息。",
|
||||||
'button_click': "window.location.href='%s'" % home_url,
|
'button_click': "window.location.href='%s'" % home_url,
|
||||||
|
@ -200,35 +204,27 @@ def reset_pwd_by_ding_callback(request):
|
||||||
return render(request, msg_template, context)
|
return render(request, msg_template, context)
|
||||||
# 解密
|
# 解密
|
||||||
crypto = Crypto(CRYPTO_KEY)
|
crypto = Crypto(CRYPTO_KEY)
|
||||||
union_id = crypto.decrypt(union_id_crypto)
|
user_id = crypto.decrypt(tmpid_crypto)
|
||||||
# 通过union_id在钉钉中拿到用户的邮箱,并格式化为username
|
# 通过user_id拿到用户的邮箱,并格式化为username
|
||||||
userid_status, user_result = ding_ops.ding_get_userid_by_union_id(union_id)
|
userid_status, user_result = _ops.get_user_detail_by_user_id(user_id)
|
||||||
if not userid_status:
|
if not userid_status:
|
||||||
context = {
|
context = {
|
||||||
'msg': '获取钉钉userid失败,错误信息:{}'.format(user_result),
|
'msg': '获取{}用户信息失败,错误信息:{}'.format(user_result, scan_params.SCAN_APP),
|
||||||
'button_click': "window.location.href='%s'" % home_url,
|
'button_click': "window.location.href='%s'" % home_url,
|
||||||
'button_display': "返回主页"
|
'button_display': "返回主页"
|
||||||
}
|
}
|
||||||
return render(request, msg_template, context)
|
return render(request, msg_template, context)
|
||||||
detail_status, ding_user_info = ding_ops.ding_get_userinfo_detail(user_result)
|
username = format2username(user_result['email'])
|
||||||
if not detail_status:
|
# 如果邮箱能提取到,则格式化之后,提取出账号提交到前端绑定
|
||||||
context = {
|
|
||||||
'msg': '获取钉钉用户信息失败,错误信息:{}'.format(ding_user_info),
|
|
||||||
'button_click': "window.location.href='%s'" % home_url,
|
|
||||||
'button_display': "返回主页"
|
|
||||||
}
|
|
||||||
return render(request, msg_template, context)
|
|
||||||
username = format2username(ding_user_info['email'])
|
|
||||||
# 如果邮箱在钉钉中能提取到,则格式化之后,提取出账号提交到前端绑定
|
|
||||||
if username:
|
if username:
|
||||||
context = {
|
context = {
|
||||||
'username': username,
|
'username': username,
|
||||||
}
|
}
|
||||||
return render(request, 'resetPassword.html', context)
|
return render(request, 'resetPassword.html', context)
|
||||||
# 否则就是钉钉中此用户未配置邮箱,返回相关提示
|
# 否则就是用户未配置邮箱,返回相关提示
|
||||||
else:
|
else:
|
||||||
context = {
|
context = {
|
||||||
'msg': "%s,您好,企业钉钉中未能找到您账号的邮箱配置,请联系HR完善信息。" % ding_user_info['name'],
|
'msg': "{},您好,企业{}中未能找到您账号的邮箱配置,请联系HR完善信息。" .format(user_result.get('name'), scan_params.SCAN_APP),
|
||||||
'button_click': "window.location.href='%s'" % home_url,
|
'button_click': "window.location.href='%s'" % home_url,
|
||||||
'button_display': "返回主页"
|
'button_display': "返回主页"
|
||||||
}
|
}
|
||||||
|
@ -238,11 +234,11 @@ def reset_pwd_by_ding_callback(request):
|
||||||
elif request.method == 'POST':
|
elif request.method == 'POST':
|
||||||
_new_password = request.POST.get('new_password').strip()
|
_new_password = request.POST.get('new_password').strip()
|
||||||
try:
|
try:
|
||||||
union_id_crypto = request.COOKIES.get('tmpid')
|
tmpid_crypto = request.COOKIES.get('tmpid')
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
union_id_crypto = None
|
tmpid_crypto = None
|
||||||
logger.error('[异常] :%s' % str(e))
|
logger.error('[异常] :%s' % str(e))
|
||||||
if not union_id_crypto:
|
if not tmpid_crypto:
|
||||||
logger.error('[异常] 请求方法:%s,请求路径:%s,未能拿到CODE或CODE己超时。' % (request.method, request.path))
|
logger.error('[异常] 请求方法:%s,请求路径:%s,未能拿到CODE或CODE己超时。' % (request.method, request.path))
|
||||||
context = {
|
context = {
|
||||||
'msg': "会话己超时,请重新扫码验证用户信息。",
|
'msg': "会话己超时,请重新扫码验证用户信息。",
|
||||||
|
@ -251,24 +247,16 @@ def reset_pwd_by_ding_callback(request):
|
||||||
}
|
}
|
||||||
return render(request, msg_template, context)
|
return render(request, msg_template, context)
|
||||||
crypto = Crypto(CRYPTO_KEY)
|
crypto = Crypto(CRYPTO_KEY)
|
||||||
union_id = crypto.decrypt(union_id_crypto)
|
user_id = crypto.decrypt(tmpid_crypto)
|
||||||
userid_status, user_result = ding_ops.ding_get_userid_by_union_id(union_id)
|
userid_status, user_result = _ops.get_user_detail_by_user_id(user_id)
|
||||||
if not userid_status:
|
if not userid_status:
|
||||||
context = {
|
context = {
|
||||||
'msg': '获取钉钉userid失败,错误信息:{}'.format(user_result),
|
'msg': '获取企业{}用户信息失败,错误信息:{}'.format(scan_params.SCAN_APP, user_result),
|
||||||
'button_click': "window.location.href='%s'" % home_url,
|
'button_click': "window.location.href='%s'" % home_url,
|
||||||
'button_display': "返回主页"
|
'button_display': "返回主页"
|
||||||
}
|
}
|
||||||
return render(request, msg_template, context)
|
return render(request, msg_template, context)
|
||||||
detail_status, ding_user_info = ding_ops.ding_get_userinfo_detail(user_result)
|
username = format2username(user_result['email'])
|
||||||
if not detail_status:
|
|
||||||
context = {
|
|
||||||
'msg': '获取钉钉用户信息失败,错误信息:{}'.format(ding_user_info),
|
|
||||||
'button_click': "window.location.href='%s'" % home_url,
|
|
||||||
'button_display': "返回主页"
|
|
||||||
}
|
|
||||||
return render(request, msg_template, context)
|
|
||||||
username = format2username(ding_user_info['email'])
|
|
||||||
if ad_ops.ad_ensure_user_by_account(username) is False:
|
if ad_ops.ad_ensure_user_by_account(username) is False:
|
||||||
context = {
|
context = {
|
||||||
'msg': "账号[%s]在AD中不存在,请确认当前钉钉扫码账号绑定的邮箱是否和您正在使用的邮箱一致?或者该账号己被禁用!\n猜测:您的账号或邮箱是否是带有数字或其它字母区分?" % username,
|
'msg': "账号[%s]在AD中不存在,请确认当前钉钉扫码账号绑定的邮箱是否和您正在使用的邮箱一致?或者该账号己被禁用!\n猜测:您的账号或邮箱是否是带有数字或其它字母区分?" % username,
|
||||||
|
@ -276,7 +264,11 @@ def reset_pwd_by_ding_callback(request):
|
||||||
'button_display': "返回主页"
|
'button_display': "返回主页"
|
||||||
}
|
}
|
||||||
return render(request, msg_template, context)
|
return render(request, msg_template, context)
|
||||||
if ad_ops.ad_get_user_status_by_account(username) == 514 or ad_ops.ad_get_user_status_by_account(username) == 66050:
|
|
||||||
|
# 514 66050是AD中账号被禁用的特定代码,这个可以在微软官网查到。
|
||||||
|
# 可能不是太准确,如果使用者能确定还有其它状态码,可以自行在此处添加
|
||||||
|
account_code = ad_ops.ad_get_user_status_by_account(username)
|
||||||
|
if account_code == 514 or account_code == 66050:
|
||||||
context = {
|
context = {
|
||||||
'msg': "此账号状态为己禁用,请联系HR确认账号是否正确。",
|
'msg': "此账号状态为己禁用,请联系HR确认账号是否正确。",
|
||||||
'button_click': "window.location.href='%s'" % home_url,
|
'button_click': "window.location.href='%s'" % home_url,
|
||||||
|
@ -297,7 +289,7 @@ def reset_pwd_by_ding_callback(request):
|
||||||
return render(request, msg_template, context)
|
return render(request, msg_template, context)
|
||||||
else:
|
else:
|
||||||
context = {
|
context = {
|
||||||
'msg': "密码未重置成功,错误信息:{}" .format(result),
|
'msg': "密码未重置成功,错误信息:{}".format(result),
|
||||||
'button_click': "window.location.href='%s'" % home_url,
|
'button_click': "window.location.href='%s'" % home_url,
|
||||||
'button_display': "返回主页"
|
'button_display': "返回主页"
|
||||||
}
|
}
|
||||||
|
@ -328,24 +320,16 @@ def unlock_account(request):
|
||||||
}
|
}
|
||||||
return render(request, msg_template, context)
|
return render(request, msg_template, context)
|
||||||
crypto = Crypto(CRYPTO_KEY)
|
crypto = Crypto(CRYPTO_KEY)
|
||||||
union_id = crypto.decrypt(_union_id_crypto)
|
user_id = crypto.decrypt(_union_id_crypto)
|
||||||
userid_status, user_result = ding_ops.ding_get_userid_by_union_id(union_id)
|
userid_status, user_info = _ops.get_user_detail_by_user_id(user_id)
|
||||||
if not userid_status:
|
if not userid_status:
|
||||||
context = {
|
context = {
|
||||||
'msg': '获取钉钉userid失败,错误信息:{}'.format(user_result),
|
'msg': '获取{}用户信息失败,错误信息:{}'.format(scan_params.SCAN_APP, user_info),
|
||||||
'button_click': "window.location.href='%s'" % home_url,
|
'button_click': "window.location.href='%s'" % home_url,
|
||||||
'button_display': "返回主页"
|
'button_display': "返回主页"
|
||||||
}
|
}
|
||||||
return render(request, msg_template, context)
|
return render(request, msg_template, context)
|
||||||
detail_status, ding_user_info = ding_ops.ding_get_userinfo_detail(user_result)
|
username = format2username(user_info['email'])
|
||||||
if not detail_status:
|
|
||||||
context = {
|
|
||||||
'msg': '获取钉钉用户信息失败,错误信息:{}'.format(ding_user_info),
|
|
||||||
'button_click': "window.location.href='%s'" % home_url,
|
|
||||||
'button_display': "返回主页"
|
|
||||||
}
|
|
||||||
return render(request, msg_template, context)
|
|
||||||
username = format2username(ding_user_info['email'])
|
|
||||||
context = {
|
context = {
|
||||||
'username': username,
|
'username': username,
|
||||||
}
|
}
|
||||||
|
@ -361,24 +345,16 @@ def unlock_account(request):
|
||||||
}
|
}
|
||||||
return render(request, msg_template, context)
|
return render(request, msg_template, context)
|
||||||
crypto = Crypto(CRYPTO_KEY)
|
crypto = Crypto(CRYPTO_KEY)
|
||||||
union_id = crypto.decrypt(_union_id_crypto)
|
user_id = crypto.decrypt(_union_id_crypto)
|
||||||
userid_status, user_result = ding_ops.ding_get_userid_by_union_id(union_id)
|
userid_status, user_info = _ops.get_user_detail_by_user_id(user_id)
|
||||||
if not userid_status:
|
if not userid_status:
|
||||||
context = {
|
context = {
|
||||||
'msg': '获取钉钉userid失败,错误信息:{}'.format(user_result),
|
'msg': '获取{}用户信息失败,错误信息:{}'.format(scan_params.SCAN_APP, user_info),
|
||||||
'button_click': "window.location.href='%s'" % home_url,
|
'button_click': "window.location.href='%s'" % home_url,
|
||||||
'button_display': "返回主页"
|
'button_display': "返回主页"
|
||||||
}
|
}
|
||||||
return render(request, msg_template, context)
|
return render(request, msg_template, context)
|
||||||
detail_status, ding_user_info = ding_ops.ding_get_userinfo_detail(user_result)
|
username = format2username(user_info['email'])
|
||||||
if not detail_status:
|
|
||||||
context = {
|
|
||||||
'msg': '获取钉钉用户信息失败,错误信息:{}'.format(ding_user_info),
|
|
||||||
'button_click': "window.location.href='%s'" % home_url,
|
|
||||||
'button_display': "返回主页"
|
|
||||||
}
|
|
||||||
return render(request, msg_template, context)
|
|
||||||
username = format2username(ding_user_info['email'])
|
|
||||||
if ad_ops.ad_ensure_user_by_account(username=username) is False:
|
if ad_ops.ad_ensure_user_by_account(username=username) is False:
|
||||||
context = {
|
context = {
|
||||||
'msg': "账号[%s]在AD中未能正确检索到,请确认当前钉钉扫码账号绑定的邮箱是否和您正在使用的邮箱一致?或者该账号己被禁用!\n猜测:您的账号或邮箱是否是带有数字或其它字母区分?" %
|
'msg': "账号[%s]在AD中未能正确检索到,请确认当前钉钉扫码账号绑定的邮箱是否和您正在使用的邮箱一致?或者该账号己被禁用!\n猜测:您的账号或邮箱是否是带有数字或其它字母区分?" %
|
||||||
|
@ -398,7 +374,7 @@ def unlock_account(request):
|
||||||
return render(request, msg_template, context)
|
return render(request, msg_template, context)
|
||||||
else:
|
else:
|
||||||
context = {
|
context = {
|
||||||
'msg': "账号未能解锁,错误信息:{}" .format(result),
|
'msg': "账号未能解锁,错误信息:{}".format(result),
|
||||||
'button_click': "window.location.href='%s'" % home_url,
|
'button_click': "window.location.href='%s'" % home_url,
|
||||||
'button_display': "返回主页"
|
'button_display': "返回主页"
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 868 KiB |
Binary file not shown.
After Width: | Height: | Size: 219 KiB |
Binary file not shown.
After Width: | Height: | Size: 116 KiB |
Binary file not shown.
After Width: | Height: | Size: 90 KiB |
Binary file not shown.
After Width: | Height: | Size: 158 KiB |
Binary file not shown.
After Width: | Height: | Size: 90 KiB |
|
@ -1,119 +0,0 @@
|
||||||
a, body, button, dd, div, dl, dt, h1, h2, h3, h4, h5, h6, input, li, ol, p, td, textarea, ul { margin: 0; padding: 0; }
|
|
||||||
body, button, input, select, textarea { font: 9pt/1.5 tahoma,arial,Hiragino Sans GB,\5b8b\4f53,sans-serif; }
|
|
||||||
button, h1, h2, h3, h4, h5, h6, input, select, textarea { font-size: 100%; }
|
|
||||||
/*background-image: linear-gradient(160deg, #2f548e 20%,#043559 80%);*/
|
|
||||||
html{height: 100%; background-image: linear-gradient(160deg, #2f548e 20%,#043559 80%);}
|
|
||||||
ol, ul { list-style: none;}
|
|
||||||
a { color: #666; text-decoration: none; }
|
|
||||||
a:hover { color: #043559; text-decoration: underline; }
|
|
||||||
body { font-size: 9pt; height: 100%;
|
|
||||||
font-family: 'microsoft yahei', sans-serif; min-width: 750pt; margin: 0; overflow: hidden}
|
|
||||||
img { border: 0; vertical-align: top; }
|
|
||||||
textarea { resize: none; }
|
|
||||||
a, button, input, select, textarea { outline: 0; }
|
|
||||||
a, button { cursor: pointer; }
|
|
||||||
button { border: none; }
|
|
||||||
.errorlist {font-size: 16px; color: #333333}
|
|
||||||
.pagewrap {height: 100% }
|
|
||||||
.main { position: relative; margin-top:0; width: 100%; height: 100%}
|
|
||||||
.header {height: 100px;margin-bottom: 5%;margin-left: 50px; background: url(/static/img/rgec.png) left center no-repeat; }
|
|
||||||
.header h1 a { display: block; }
|
|
||||||
.content { overflow: hidden; margin-left: 10% }
|
|
||||||
.content .con_left { float: left; height: 450px; width: 50%; margin-top: 65px}
|
|
||||||
/*.content .con_left .box {position: absolute; width: 400px; height:400px; left: 50%; right: 50%; margin-left: -100px; margin-right: -100px;}*/
|
|
||||||
.content .con_left p { padding: 0 0 3px; width: 10pc; color: #040000; font-size: 1pc; font-family: 'microsoft yahei', sans-serif; }
|
|
||||||
.content .con_left a { padding: 0 0 0 2pc; color: #2f548e; text-decoration: underline; font-size: 1pc; font-family: 'microsoft yahei', sans-serif; }
|
|
||||||
.content .con_right { float: left; margin: 65px 0 0; width: 28pc; height: 450px; border: 1px solid #dedede; background: #fff; }
|
|
||||||
.content .con_right .con_r_top { padding: 0 0 0 39px; width: 409px; height: 110px; border-top: 8px solid #2e558e; }
|
|
||||||
.content .con_right .con_r_top .left, .content .con_right .con_r_top .right { float: left; padding: 35px 0 0; width: 186px; height: 35px; text-align: center; text-decoration: none; font-size: 18px; font-family: "微软雅黑"; }
|
|
||||||
.content .con_right .con_r_top .left { border-bottom: 2px solid #dedede; color: #999; }
|
|
||||||
.content .con_right ul .con_r_left .erweima { text-align: center; }
|
|
||||||
.content .con_right ul .con_r_left p {color: #2f548e; font-size: 25px; font-family: 'microsoft yahei', sans-serif; }
|
|
||||||
.content .con_right ul .con_r_left .user input { margin: 0 0 1px -1px; padding-left: 7px; width: 324px; height: 33px; border: 1px solid #dedede; color: #999; font-size: 14px; font-family: "微软雅黑"; line-height: 2pc; }
|
|
||||||
.content .con_right ul .con_r_left .user { padding: 0 0 0 39px; }
|
|
||||||
.content .con_right ul .con_r_left .user ul{font-size: 16px; color: #333333}
|
|
||||||
.content .con_right ul .con_r_left .user li{font-size: 16px; color: #333333}
|
|
||||||
.content .con_right ul .con_r_left .user .user-icon { float: left; width: 36px; height: 35px; background: url(../img/user-icon.jpg) left top no-repeat; }
|
|
||||||
.content .con_right ul .con_r_left .user .mima-icon { float: left; width: 36px; height: 35px; background: url(../img/mima-icon.jpg) left top no-repeat; }
|
|
||||||
.content .con_right ul .con_r_left .user .unlock-icon { float: left; width: 36px; height: 35px; background: url(../img/unlock.jpg) left top no-repeat; }
|
|
||||||
.content .con_right ul .con_r_left p { overflow: hidden; padding: 0 39px 37px; color: #666; font-size: 13px; font-family: 'microsoft yahei', sans-serif; }
|
|
||||||
.content .con_right ul .con_r_left p .mima { float: left; padding-left: 5px; text-decoration: none; }
|
|
||||||
.content .con_right ul .con_r_left p .zhuce { float: right; text-decoration: none; }
|
|
||||||
.content .con_right ul .con_r_left button { margin: 0 0 0 75pt; width: 250px; height: 44px; background: #2e558e; color: #fff; font-size: 1pc; font-family: 'microsoft yahei', sans-serif; }
|
|
||||||
.content .con_right .con_r_top .right { border-bottom: 2px solid #2e558e; color: #333; }
|
|
||||||
.content .con_right ul .con_r_right .user input { margin: 0 0 1px -1px; padding-left: 7px; width: 324px; height: 33px; border: 1px solid #dedede; color: #999; font-size: 14px; font-family: "微软雅黑"; line-height: 2pc; }
|
|
||||||
.content .con_right ul .con_r_right .user { padding: 0 0 0 39px; }
|
|
||||||
.content .con_right ul .con_r_right .user ul{font-size: 16px; color: #333333}
|
|
||||||
.content .con_right ul .con_r_right .user li{font-size: 16px; color: #333333}
|
|
||||||
.content .con_right ul .con_r_right .user .user-icon { float: left; width: 36px; height: 35px; background: url(../img/user-icon.jpg) left top no-repeat; }
|
|
||||||
.content .con_right ul .con_r_right .user .mima-icon { float: left; width: 36px; height: 35px; background: url(../img/mima-icon.jpg) left top no-repeat; }
|
|
||||||
.content .con_right ul .con_r_right .user .unlock-icon { float: left; width: 36px; height: 35px; background: url(../img/unlock.jpg) left top no-repeat; }
|
|
||||||
.content .con_right ul .con_r_right p { overflow: hidden; padding: 0 39px 37px; color: #666; font-size: 13px; font-family: 'microsoft yahei', sans-serif; }
|
|
||||||
.content .con_right ul .con_r_right p .mima { float: left; padding-left: 5px; text-decoration: none; }
|
|
||||||
.content .con_right ul .con_r_right p .zhuce { float: right; text-decoration: none; }
|
|
||||||
.content .con_right ul .con_r_right button { margin: 0 0 0 75pt; width: 250px; height: 44px; background: #2e558e; color: #fff; font-size: 1pc; font-family: 'microsoft yahei', sans-serif; }
|
|
||||||
.content .con_right ul .con_r_left { display: none; }
|
|
||||||
.con_right ul .con_r_left .erweima { position: relative; margin: 0 auto; width: 365px; height: 330px; }
|
|
||||||
.qrcode { position: absolute; top: 0; left: 0; width: 174px; height: 11pc; }
|
|
||||||
.divimg { position: absolute; top: 50%; left: 50%; z-index: 100; overflow: hidden; margin-top: -15px; margin-left: -30px; padding: 1px; width: 60px; height: 30px; border: 1px solid #eee; border-radius: .5rem; background: #fff; opacity: .9; filter: alpha(opacity=90); -moz-opacity: .9; }
|
|
||||||
.content .con_right ul .con_r_right .user .yanzheng { width: 150px; margin: 0 5px 10px 1px; padding-left: 5px; }
|
|
||||||
.content .con_right ul .con_r_right .user .next { font-size: 12px; width: 40px; height: 33px; float: right; margin-right: 40px; }
|
|
||||||
.content .con_right .con_r_top { *height: 90px; }
|
|
||||||
|
|
||||||
.autoWidth{margin:0 auto;min-width:1000px;max-width:1200px}
|
|
||||||
.auto{margin:0 auto;min-width:1000px;max-width:1200px}
|
|
||||||
@media screen and (max-width:1233px){.auto{padding-left:10px}
|
|
||||||
}
|
|
||||||
.clearfix:after,.clearfix:before{display:table;line-height:0;content:""}
|
|
||||||
.clearfix:after{clear:both}
|
|
||||||
.clear-float{clear:both}
|
|
||||||
|
|
||||||
.footer{background-color:#009fd9;font-family: 'microsoft yahei', sans-serif; }
|
|
||||||
.footer-floor1{width:100%;padding:36px 0 60px}
|
|
||||||
.footer-list{width:69%;height:100%;float:left}
|
|
||||||
.footer-list ul{float:left;margin-right:13%}
|
|
||||||
.footer-list .flist-4{margin-right:0}
|
|
||||||
.footer-list li{line-height:32px}
|
|
||||||
.footer-list li a{color:#b6e2f2;font-size:12px;text-decoration:none}
|
|
||||||
.footer-list li a:hover{text-decoration:underline;color:#fff}
|
|
||||||
.footer-list .flist-title{font-size:16px;color:#fff;margin-bottom:15px}
|
|
||||||
.footer-floor2{width:100%;border-top:1px solid #4cc3ed;padding:20px 0;text-align:center}
|
|
||||||
.footer-floor2 p{text-align:center;color:#b6e2f2;font-size:12px;line-height:30px}
|
|
||||||
.footer-floor2 p span{font-family:PingFangSC-Light,'helvetica neue','hiragino sans gb',tahoma,'microsoft yahei ui','microsoft yahei',sans-serif}
|
|
||||||
.footer-floor2 a{color:#b6e2f2}
|
|
||||||
.footer-floor2 a:hover{color:#a8d0e0;text-decoration:underline}
|
|
||||||
.foot-link{margin:0 15px;text-decoration:none;color:#b6e2f2}
|
|
||||||
.foot-link:hover{text-decoration:underline}
|
|
||||||
.footer-right{width:300px;float:right}
|
|
||||||
.telephone{width:100%;height:32px;line-height:32px;color:#fff}
|
|
||||||
.telephone .tel-number{font-size:30px;font-weight:400;text-align:right}
|
|
||||||
.official-plat{width:100%;height:100%;margin-top:20px;position:relative}
|
|
||||||
.official-plat ul{float:right;margin-top:7px}
|
|
||||||
.official-plat ul li{height:45px}
|
|
||||||
.official-plat ul a{display:inline-block;height:32px;width:100%;line-height:32px;color:#fff;text-decoration:none;font-size:12px}
|
|
||||||
.official-plat>p{display:inline-block;width:132px;height:132px;border:1px solid #ddd;background-color:#fff}
|
|
||||||
#wx-corner{border:10px solid transparent;border-left:10px solid #fff;position:absolute;top:12px;right:-20px;z-index:10}
|
|
||||||
#wb-corner{border:10px solid transparent;border-left:10px solid #fff;position:absolute;top:58px;right:-20px;z-index:10}
|
|
||||||
.five-superiority{width:100%;border-bottom:1px solid #27aede;padding:10px 0 20px}
|
|
||||||
.five-superiority-list li{float:left;width:20%;height:36px;text-align:center;border-left:1px solid #27aede}
|
|
||||||
.five-superiority-list li:first-child{border-left:none}
|
|
||||||
.five-superiority-list li a{display:inline-block;position:relative;width:100%;height:36px;line-height:36px;background:no-repeat 2% center;text-indent:2em;color:#fff;font-size:16px}
|
|
||||||
.five-superiority-list li a:hover{color:#bfe7f5}
|
|
||||||
.five-superiority-list li a.superiority-text{text-indent:4em}
|
|
||||||
.compensate_ico .superiority-icon{background-position:0 0}
|
|
||||||
.compensate_ico:hover .superiority-icon{background-position:0 -50px}
|
|
||||||
.retreat_ico .superiority-icon{background-position:0 -100px}
|
|
||||||
.retreat_ico:hover .superiority-icon{background-position:0 -150px}
|
|
||||||
.technology_ico .superiority-icon{background-position:0 -200px}
|
|
||||||
.technology_ico:hover .superiority-icon{background-position:0 -250px}
|
|
||||||
.prepare_ico .superiority-icon{background-position:0 -300px}
|
|
||||||
.prepare_ico:hover .superiority-icon{background-position:0 -350px}
|
|
||||||
.service_ico .superiority-icon{background-position:0 -400px}
|
|
||||||
.service_ico:hover .superiority-icon{background-position:0 -450px}
|
|
||||||
.marquee-box{overflow:hidden;width:100%;position:absolute;left:0;top:0}
|
|
||||||
.marquee{width:8000%;height:60px}
|
|
||||||
.wave-list-box{float:left}
|
|
||||||
.wave-list-box ul{float:left;height:60px;overflow:hidden;zoom:1}
|
|
||||||
.wave-list-box ul li{height:60px;width:100%;float:left;line-height:30px;list-style:none}
|
|
||||||
.wave-box{position:relative;height:60px;background:#fff}
|
|
||||||
|
|
|
@ -1,66 +0,0 @@
|
||||||
*{margin:0;padding:0;box-sizing:border-box;list-style:none}
|
|
||||||
html{height: 100%; width:100%}
|
|
||||||
body{font-family:"Microsoft Yahei";min-width:1000px}
|
|
||||||
a{outline:0;text-decoration:none}
|
|
||||||
strong{font-weight:400}
|
|
||||||
.strong{font-weight:700}
|
|
||||||
::selection{background:#1EACDF;color:#fff}
|
|
||||||
img{border:0}
|
|
||||||
::-moz-selection{background:#1EACDF;color:#fff}
|
|
||||||
::-webkit-selection{background:#1EACDF;color:#fff}
|
|
||||||
.autoWidth{margin:0 auto;min-width:1000px;max-width:1200px}
|
|
||||||
.auto{margin:0 auto;min-width:1000px;max-width:1200px}
|
|
||||||
@media screen and (max-width:1233px){.auto{padding-left:10px}
|
|
||||||
}
|
|
||||||
.clearfix:after,.clearfix:before{display:table;line-height:0;content:""}
|
|
||||||
.clearfix:after{clear:both}
|
|
||||||
.clear-float{clear:both}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.footer{background-color:#009fd9;font-family:"Microsoft Yahei"}
|
|
||||||
.footer-floor1{width:100%;padding:36px 0 60px}
|
|
||||||
.footer-list{width:69%;height:100%;float:left}
|
|
||||||
.footer-list ul{float:left;margin-right:13%}
|
|
||||||
.footer-list .flist-4{margin-right:0}
|
|
||||||
.footer-list li{line-height:32px}
|
|
||||||
.footer-list li a{color:#b6e2f2;font-size:12px;text-decoration:none}
|
|
||||||
.footer-list li a:hover{text-decoration:underline;color:#fff}
|
|
||||||
.footer-list .flist-title{font-size:16px;color:#fff;margin-bottom:15px}
|
|
||||||
.footer-floor2{width:100%;border-top:1px solid #4cc3ed;padding:20px 0;text-align:center}
|
|
||||||
.footer-floor2 p{text-align:center;color:#b6e2f2;font-size:12px;line-height:30px}
|
|
||||||
.footer-floor2 p span{font-family:PingFangSC-Light,'helvetica neue','hiragino sans gb',tahoma,'microsoft yahei ui','microsoft yahei',simsun,sans-serif}
|
|
||||||
.footer-floor2 a{color:#b6e2f2}
|
|
||||||
.footer-floor2 a:hover{color:#a8d0e0;text-decoration:underline}
|
|
||||||
.foot-link{margin:0 15px;text-decoration:none;color:#b6e2f2}
|
|
||||||
.foot-link:hover{text-decoration:underline}
|
|
||||||
.footer-right{width:300px;float:right}
|
|
||||||
.official-plat{width:100%;height:100%;margin-top:20px;position:relative}
|
|
||||||
.official-plat ul{float:right;margin-top:7px}
|
|
||||||
.official-plat ul li{height:45px}
|
|
||||||
.official-plat ul a{display:inline-block;height:32px;width:100%;line-height:32px;color:#fff;text-decoration:none;font-size:12px}
|
|
||||||
.official-plat>p{display:inline-block;width:132px;height:132px;border:1px solid #ddd;background-color:#fff}
|
|
||||||
#wx-corner{border:10px solid transparent;border-left:10px solid #fff;position:absolute;top:12px;right:-20px;z-index:10}
|
|
||||||
#wb-corner{border:10px solid transparent;border-left:10px solid #fff;position:absolute;top:58px;right:-20px;z-index:10}
|
|
||||||
.five-superiority{width:100%;border-bottom:1px solid #27aede;padding:10px 0 20px}
|
|
||||||
.five-superiority-list li{float:left;width:20%;height:36px;text-align:center;border-left:1px solid #27aede}
|
|
||||||
.five-superiority-list li:first-child{border-left:none}
|
|
||||||
.five-superiority-list li a{display:inline-block;position:relative;width:100%;height:36px;line-height:36px;background:no-repeat 2% center;text-indent:2em;color:#fff;font-size:16px}
|
|
||||||
.five-superiority-list li a:hover{color:#bfe7f5}
|
|
||||||
.five-superiority-list li a.superiority-text{text-indent:4em}
|
|
||||||
.compensate_ico .superiority-icon{background-position:0 0}
|
|
||||||
.compensate_ico:hover .superiority-icon{background-position:0 -50px}
|
|
||||||
.retreat_ico .superiority-icon{background-position:0 -100px}
|
|
||||||
.retreat_ico:hover .superiority-icon{background-position:0 -150px}
|
|
||||||
.technology_ico .superiority-icon{background-position:0 -200px}
|
|
||||||
.technology_ico:hover .superiority-icon{background-position:0 -250px}
|
|
||||||
.prepare_ico .superiority-icon{background-position:0 -300px}
|
|
||||||
.prepare_ico:hover .superiority-icon{background-position:0 -350px}
|
|
||||||
.service_ico .superiority-icon{background-position:0 -400px}
|
|
||||||
.service_ico:hover .superiority-icon{background-position:0 -450px}
|
|
||||||
.marquee-box{overflow:hidden;width:100%;position:absolute;left:0;top:0}
|
|
||||||
.marquee{width:8000%;height:60px}
|
|
||||||
.wave-list-box{float:left}
|
|
||||||
.wave-list-box ul{float:left;height:60px;overflow:hidden;zoom:1}
|
|
||||||
.wave-list-box ul li{height:60px;width:100%;float:left;line-height:30px;list-style:none}
|
|
||||||
.wave-box{position:relative;height:60px;background:#fff}
|
|
|
@ -1 +0,0 @@
|
||||||
"use strict";window.bubbly=function(t){var n=t||{},o=function(){return Math.random()},r=n.canvas||document.createElement("canvas"),e=r.width,a=r.height;null===r.parentNode&&(r.setAttribute("style","position:fixed;z-index:-1;left:0;top:0;min-width:100vw;min-height:100vh;"),e=r.width=window.innerWidth,a=r.height=window.innerHeight,document.body.appendChild(r));var i=r.getContext("2d");i.shadowColor=n.shadowColor||"#fff",i.shadowBlur=n.blur||4;var l=i.createLinearGradient(0,0,e,a);l.addColorStop(0,n.colorStart||"#2AE"),l.addColorStop(1,n.colorStop||"#17B");for(var c=n.bubbles||Math.floor(.02*(e+a)),u=[],d=0;d<c;d++)u.push({f:(n.bubbleFunc||function(){return"hsla(0, 0%, 100%, "+.1*o()+")"}).call(),x:o()*e,y:o()*a,r:(n.radiusFunc||function(){return 4+o()*e/25}).call(),a:(n.angleFunc||function(){return o()*Math.PI*2}).call(),v:(n.velocityFunc||function(){return.1+.5*o()}).call()});!function t(){if(null===r.parentNode)return cancelAnimationFrame(t);!1!==n.animate&&requestAnimationFrame(t),i.globalCompositeOperation="source-over",i.fillStyle=l,i.fillRect(0,0,e,a),i.globalCompositeOperation=n.compose||"lighter",u.forEach(function(t){i.beginPath(),i.arc(t.x,t.y,t.r,0,2*Math.PI),i.fillStyle=t.f,i.fill(),t.x+=Math.cos(t.a)*t.v,t.y+=Math.sin(t.a)*t.v,t.x-t.r>e&&(t.x=-t.r),t.x+t.r<0&&(t.x=e+t.r),t.y-t.r>a&&(t.y=-t.r),t.y+t.r<0&&(t.y=a+t.r)})}()};
|
|
File diff suppressed because one or more lines are too long
|
@ -1,41 +0,0 @@
|
||||||
$(document).ready(function () {
|
|
||||||
|
|
||||||
|
|
||||||
$(".official-plat ul li:first-child").hover(function () {
|
|
||||||
$(".weixin").show();
|
|
||||||
$(".weibo").hide();
|
|
||||||
});
|
|
||||||
$("li[title='点击打开官方微博']").hover(function () {
|
|
||||||
$(".weixin").hide();
|
|
||||||
$(".weibo").show();
|
|
||||||
});
|
|
||||||
|
|
||||||
//href="#a_null"的统一设置为无效链接
|
|
||||||
$("a[href='#a_null']").click(function () {
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
//波浪动画
|
|
||||||
$(function () {
|
|
||||||
var marqueeScroll = function (id1, id2, id3, timer) {
|
|
||||||
var $parent = $("#" + id1);
|
|
||||||
var $goal = $("#" + id2);
|
|
||||||
var $closegoal = $("#" + id3);
|
|
||||||
$closegoal.html($goal.html());
|
|
||||||
function Marquee() {
|
|
||||||
if (parseInt($parent.scrollLeft()) - $closegoal.width() >= 0) {
|
|
||||||
$parent.scrollLeft(parseInt($parent.scrollLeft()) - $goal.width());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$parent.scrollLeft($parent.scrollLeft() + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setInterval(Marquee, timer);
|
|
||||||
}
|
|
||||||
var marqueeScroll1 = new marqueeScroll("marquee-box", "wave-list-box1", "wave-list-box2", 20);
|
|
||||||
var marqueeScroll2 = new marqueeScroll("marquee-box3", "wave-list-box4", "wave-list-box5", 40);
|
|
||||||
});
|
|
|
@ -0,0 +1,98 @@
|
||||||
|
{% load static %}
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||||
|
<title>密码自助服务</title>
|
||||||
|
<link type="text/css" rel="stylesheet" href="{% static 'css/login.css' %}">
|
||||||
|
<script type="text/javascript" src="{% static 'js/jquery-1.8.3.min.js' %}"></script>
|
||||||
|
<script type="text/javascript" src="{% static 'js/check.js' %}"></script>
|
||||||
|
<script src="https://rescdn.qqmail.com/node/ww/wwopenmng/js/sso/wwLogin-1.0.0.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="pagewrap">
|
||||||
|
<div class="main">
|
||||||
|
<div class="header">
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
<div class="con_left">
|
||||||
|
<div style="margin: 0 auto; width:100%; height: 200px; line-height: 200px;" align="center">
|
||||||
|
<p style="margin: 0 auto; color: #fdfdfe; font-size: 36px; width:100%; ">「域账号或邮箱」<small>密码自助平台</small></p>
|
||||||
|
</div>
|
||||||
|
<div style="margin: 0 auto; width:400px; height: 240px;">
|
||||||
|
<p style="margin: 0 auto; color: #fdfdfe; font-size: 16px; width:100%;
|
||||||
|
">提示:新密码要求满足8至30位长度(不包含空格),至少包含大小写字母及数字组成。</p>
|
||||||
|
<p style="margin: 0 auto; color: #fdfdfe; font-size: 16px; width:100%;
|
||||||
|
">如果密码己遗忘,可点击[重置/解锁],使用钉钉扫描验证后直接重置密码。</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="con_right">
|
||||||
|
<div class="con_r_top">
|
||||||
|
<a href="javascript:" class="right"
|
||||||
|
style="color: rgb(51, 51, 51); border-bottom-width: 2px; border-bottom-style: solid; border-bottom-color: rgb(46, 85, 142);">修改密码</a>
|
||||||
|
<a href="javascript:" class="left"
|
||||||
|
style="color: rgb(153, 153, 153); border-bottom-width: 2px; border-bottom-style: solid; border-bottom-color: rgb(222, 222, 222);">重置/解锁</a>
|
||||||
|
</div>
|
||||||
|
<ul>
|
||||||
|
<li class="con_r_right" style="display: block;">
|
||||||
|
<form name="modifypwd" method="post" action="" autocomplete="off">
|
||||||
|
{% csrf_token %}
|
||||||
|
<div class="user">
|
||||||
|
<div><span class="user-icon"></span>
|
||||||
|
<input type="text" id="username" name="username" placeholder="格式:abc\lisi、lisi、lisi@abc.com" value="">
|
||||||
|
</div>
|
||||||
|
<div><span class="mima-icon"></span>
|
||||||
|
<input type="password" id="old_password" name="old_password"
|
||||||
|
placeholder=" 输入旧密码" value="">
|
||||||
|
</div>
|
||||||
|
<div><span class="mima-icon"></span>
|
||||||
|
<input type="password" id="new_password" name="new_password"
|
||||||
|
placeholder=" 输入新密码" value="">
|
||||||
|
</div>
|
||||||
|
<div><span class="mima-icon"></span>
|
||||||
|
<input type="password" id="ensure_password" name="ensure_password"
|
||||||
|
placeholder=" 再次输入新密码" value="">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<button id="btn_modify" type="submit">修改密码</button>
|
||||||
|
</form>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="con_r_left" style="display: none;">
|
||||||
|
<div style="margin-top: -30px" class="erweima">
|
||||||
|
<div style="width: 300px; height: 300px; margin: 0 auto" id="we_code"></div>
|
||||||
|
<script type="text/javascript">
|
||||||
|
let home_url = "{{ home_url }}";
|
||||||
|
let app_id = "{{ app_id }}";
|
||||||
|
let agent_id = "{{ agent_id }}"
|
||||||
|
let redirect_url = encodeURIComponent(home_url + '/callbackCheck');
|
||||||
|
window.WwLogin({
|
||||||
|
id: "we_code",
|
||||||
|
appid: app_id,
|
||||||
|
agentid: agent_id,
|
||||||
|
redirect_uri: redirect_url,
|
||||||
|
// 样式使用base64加密,而不使用https的方式
|
||||||
|
href: 'data:text/css;base64, ' +
|
||||||
|
'LmltcG93ZXJCb3ggLnRpdGxlIHtkaXNwbGF5OiBub25lO30KLmltcG93ZXJCb3ggLnFyY29kZSB7d2lkdGg6IDIyMHB4O30KLmltcG93ZXJCb3ggLmluZm8ge3dpZHRoOiAyMjBweDt9Ci5zdGF0dXNfaWNvbiB7ZGlzcGxheTogbm9uZSAgIWltcG9ydGFudH0KLmltcG93ZXJCb3ggLnN0YXR1cy5zdGF0dXNfYnJvd3NlciB7ZGlzcGxheTogbm9uZTt9Ci5pbXBvd2VyQm94IC5zdGF0dXMge3RleHQtYWxpZ246IGNlbnRlcjt9'
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</div>
|
||||||
|
<div style="height: 70px; margin-top: -30px">
|
||||||
|
<p style="font-size: 18px; color: #2e558e" align="center">企业微信扫码验证用户信息</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script type="text/javascript">
|
||||||
|
window.onload = function () {
|
||||||
|
if (!!window.ActiveXObject || "ActiveXObject" in window)
|
||||||
|
alert("您当前使用的浏览器为IE或IE内核,因为IE各种体验问题,本网站不对IE兼容。\n为能正常使用密码自助修改服务,请更换谷歌、火狐等非IE核心的浏览器。\n如果是360、Maxthon等这类双核心浏览器,请切换至[极速模式]亦可。")
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -46,7 +46,7 @@ class AdOps(object):
|
||||||
self.authentication = authentication
|
self.authentication = authentication
|
||||||
self.auto_bind = auto_bind
|
self.auto_bind = auto_bind
|
||||||
|
|
||||||
server = Server(host='%s' % AD_HOST, use_ssl=self.use_ssl, port=port, get_info=ALL)
|
server = Server(host='%s' % AD_HOST, connect_timeout=1, use_ssl=self.use_ssl, port=port, get_info=ALL)
|
||||||
try:
|
try:
|
||||||
self.conn = Connection(server, auto_bind=self.auto_bind, user=r'{}\{}'.format(self.domain, self.user), password=self.password,
|
self.conn = Connection(server, auto_bind=self.auto_bind, user=r'{}\{}'.format(self.domain, self.user), password=self.password,
|
||||||
authentication=self.authentication, raise_exceptions=True)
|
authentication=self.authentication, raise_exceptions=True)
|
||||||
|
@ -115,7 +115,7 @@ class AdOps(object):
|
||||||
|
|
||||||
def ad_get_user_dn_by_account(self, username):
|
def ad_get_user_dn_by_account(self, username):
|
||||||
"""
|
"""
|
||||||
通过mail查询某个用户的完整DN
|
通过username查询某个用户的完整DN
|
||||||
:param username:
|
:param username:
|
||||||
:return: DN
|
:return: DN
|
||||||
"""
|
"""
|
||||||
|
@ -179,7 +179,7 @@ class AdOps(object):
|
||||||
|
|
||||||
def ad_get_user_locked_status_by_account(self, username):
|
def ad_get_user_locked_status_by_account(self, username):
|
||||||
"""
|
"""
|
||||||
通过mail获取某个用户账号是否被锁定
|
通过username获取某个用户账号是否被锁定
|
||||||
:param username:
|
:param username:
|
||||||
:return: 如果结果是1601-01-01说明账号未锁定,返回0
|
:return: 如果结果是1601-01-01说明账号未锁定,返回0
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -9,47 +9,31 @@ from urllib.parse import quote
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
from dingtalk.client import AppKeyClient
|
from dingtalk.client import AppKeyClient
|
||||||
|
from pwdselfservice import cache_storage
|
||||||
|
|
||||||
from pwdselfservice.local_settings import DING_APP_KEY, DING_APP_SECRET, DING_CORP_ID, DING_URL, DING_MO_APP_ID, DING_MO_APP_SECRET
|
from pwdselfservice.local_settings import DING_APP_KEY, DING_APP_SECRET, DING_CORP_ID, DING_URL, DING_MO_APP_ID, DING_MO_APP_SECRET
|
||||||
|
|
||||||
|
|
||||||
class DingDingOps(object):
|
class DingDingOps(object):
|
||||||
def __init__(self, corp_id=DING_CORP_ID, app_key=DING_APP_KEY, app_secret=DING_APP_SECRET, mo_app_id=DING_MO_APP_ID, mo_app_secret=DING_MO_APP_SECRET):
|
def __init__(self, corp_id=DING_CORP_ID, app_key=DING_APP_KEY, app_secret=DING_APP_SECRET, mo_app_id=DING_MO_APP_ID, mo_app_secret=DING_MO_APP_SECRET, storage=cache_storage):
|
||||||
self.corp_id = corp_id
|
self.corp_id = corp_id
|
||||||
self.app_key = app_key
|
self.app_key = app_key
|
||||||
self.app_secret = app_secret
|
self.app_secret = app_secret
|
||||||
self.mo_app_id = mo_app_id
|
self.mo_app_id = mo_app_id
|
||||||
self.mo_app_secret = mo_app_secret
|
self.mo_app_secret = mo_app_secret
|
||||||
|
self.storage = storage
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def ding_client_connect(self):
|
def _client_connect(self):
|
||||||
"""
|
"""
|
||||||
钉钉连接器
|
钉钉连接器
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
return AppKeyClient(corp_id=self.corp_id, app_key=self.app_key, app_secret=self.app_secret)
|
return AppKeyClient(corp_id=self.corp_id, app_key=self.app_key, app_secret=self.app_secret)
|
||||||
|
|
||||||
@property
|
def get_union_id_by_code(self, code):
|
||||||
def ding_get_access_token(self):
|
|
||||||
"""
|
"""
|
||||||
获取企业内部应用的access_token
|
通过移动应用接入扫码返回的临时授权码,使用临时授权码换取用户的unionid
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
return self.ding_client_connect.access_token
|
|
||||||
|
|
||||||
def ding_get_dept_user_list_detail(self, dept_id, offset, size):
|
|
||||||
"""
|
|
||||||
获取部门中的用户列表详细清单
|
|
||||||
:param dept_id: 部门ID
|
|
||||||
:param offset: 偏移量(可理解为步进量)
|
|
||||||
:param size: 一次查询多少个
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
return self.ding_client_connect.user.list(department_id=dept_id, offset=offset, size=size)
|
|
||||||
|
|
||||||
def ding_get_union_id_by_code(self, code):
|
|
||||||
"""
|
|
||||||
通过移动应用接入扫码返回的临时授权码,获取用户的unionid
|
|
||||||
:param code:
|
:param code:
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
|
@ -68,57 +52,36 @@ class DingDingOps(object):
|
||||||
json=dict(tmp_auth_code=code),
|
json=dict(tmp_auth_code=code),
|
||||||
)
|
)
|
||||||
resp = resp.json()
|
resp = resp.json()
|
||||||
print(resp)
|
|
||||||
try:
|
try:
|
||||||
if resp['errcode'] != 0:
|
if resp['errcode'] != 0:
|
||||||
return False, 'ding_get_union_id_by_code: %s' % str(resp)
|
return False, 'get_union_id_by_code: %s' % str(resp)
|
||||||
else:
|
else:
|
||||||
return True, resp["user_info"]["unionid"]
|
return True, resp["user_info"]["unionid"]
|
||||||
except Exception:
|
except Exception:
|
||||||
return False, 'ding_get_union_id_by_code: %s' % str(resp)
|
return False, 'get_union_id_by_code: %s' % str(resp)
|
||||||
|
|
||||||
def ding_get_userid_by_union_id(self, union_id):
|
def get_user_id_by_code(self, code):
|
||||||
"""
|
"""
|
||||||
通过unionid获取用户的userid
|
通过code获取用户的userid
|
||||||
:param union_id: 用户在当前钉钉开放平台账号范围内的唯一标识
|
:param id: 用户在当前钉钉开放平台账号范围内的唯一标识
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
try:
|
_status, union_id = self.get_union_id_by_code(code)
|
||||||
return True, self.ding_client_connect.user.get_userid_by_unionid(union_id)['userid']
|
if _status:
|
||||||
except Exception as e:
|
return True, self._client_connect.user.get_userid_by_unionid(union_id).get('userid')
|
||||||
return False, 'ding_get_union_id_by_code: %s' % str(e)
|
else:
|
||||||
|
return False, 'get_user_id_by_code: %s' % str(union_id)
|
||||||
|
|
||||||
except (KeyError, IndexError) as k_error:
|
def get_user_detail_by_user_id(self, user_id):
|
||||||
return False, 'ding_get_union_id_by_code: %s' % str(k_error)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def ding_get_org_user_count(self):
|
|
||||||
"""
|
|
||||||
企业员工数量
|
|
||||||
only_active – 是否包含未激活钉钉的人员数量
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
return self.ding_client_connect.user.get_org_user_count('only_active')
|
|
||||||
|
|
||||||
def ding_get_userinfo_detail(self, user_id):
|
|
||||||
"""
|
"""
|
||||||
通过user_id 获取用户详细信息
|
通过user_id 获取用户详细信息
|
||||||
user_id – 用户ID
|
user_id – 用户ID
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
return True, self.ding_client_connect.user.get(user_id)
|
return True, self._client_connect.user.get(user_id)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return False, 'ding_get_union_id_by_code: %s' % str(e)
|
return False, 'get_user_detail_by_user_id: %s' % str(e)
|
||||||
|
|
||||||
except (KeyError, IndexError) as k_error:
|
except (KeyError, IndexError) as k_error:
|
||||||
return False, 'ding_get_union_id_by_code: %s' % str(k_error)
|
return False, 'get_user_detail_by_user_id: %s' % str(k_error)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
start = time.time()
|
|
||||||
d = DingDingOps().ding_client_connect
|
|
||||||
unicode = ''
|
|
||||||
# print(d.)
|
|
||||||
end = time.time()
|
|
||||||
print("running:" + str(round((end - start), 3)))
|
|
||||||
|
|
|
@ -24,12 +24,22 @@ def format2username(account):
|
||||||
elif re.fullmatch(domain_compile, account):
|
elif re.fullmatch(domain_compile, account):
|
||||||
return re.fullmatch(domain_compile, account).group(2)
|
return re.fullmatch(domain_compile, account).group(2)
|
||||||
else:
|
else:
|
||||||
return account
|
return account.lower()
|
||||||
else:
|
else:
|
||||||
raise NameError("输入的账号不能为空..")
|
raise NameError("输入的账号不能为空..")
|
||||||
|
|
||||||
|
|
||||||
|
def get_user_is_active(user_info):
|
||||||
|
try:
|
||||||
|
return True, user_info.get('active') or user_info.get('status')
|
||||||
|
except Exception as e:
|
||||||
|
return False, 'get_user_is_active: %s' % str(e)
|
||||||
|
|
||||||
|
except (KeyError, IndexError) as k_error:
|
||||||
|
return False, 'get_user_is_active: %s' % str(k_error)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
user = 'aaa\jf.com'
|
user = 'jf.com\XiangLe'
|
||||||
username = format2username(user)
|
username = format2username(user)
|
||||||
print(username)
|
print(username)
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import absolute_import, unicode_literals
|
||||||
|
|
||||||
|
|
||||||
|
class BaseStorage(object):
|
||||||
|
|
||||||
|
def get(self, key, default=None):
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def set(self, key, value, ttl=None):
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def delete(self, key):
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def __getitem__(self, key):
|
||||||
|
self.get(key)
|
||||||
|
|
||||||
|
def __setitem__(self, key, value):
|
||||||
|
self.set(key, value)
|
||||||
|
|
||||||
|
def __delitem__(self, key):
|
||||||
|
self.delete(key)
|
|
@ -0,0 +1,58 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import absolute_import, unicode_literals
|
||||||
|
|
||||||
|
import inspect
|
||||||
|
|
||||||
|
from utils.storage import BaseStorage
|
||||||
|
|
||||||
|
|
||||||
|
def _is_cache_item(obj):
|
||||||
|
return isinstance(obj, CacheItem)
|
||||||
|
|
||||||
|
|
||||||
|
class CacheItem(object):
|
||||||
|
|
||||||
|
def __init__(self, cache=None, name=None):
|
||||||
|
self.cache = cache
|
||||||
|
self.name = name
|
||||||
|
|
||||||
|
def key_name(self, key):
|
||||||
|
if isinstance(key, (tuple, list)):
|
||||||
|
key = ':'.join(key)
|
||||||
|
|
||||||
|
k = '{0}:{1}'.format(self.cache.prefix, self.name)
|
||||||
|
if key is not None:
|
||||||
|
k = '{0}:{1}'.format(k, key)
|
||||||
|
return k
|
||||||
|
|
||||||
|
def get(self, key=None, default=None):
|
||||||
|
return self.cache.storage.get(self.key_name(key), default)
|
||||||
|
|
||||||
|
def set(self, key=None, value=None, ttl=None):
|
||||||
|
return self.cache.storage.set(self.key_name(key), value, ttl)
|
||||||
|
|
||||||
|
def delete(self, key=None):
|
||||||
|
return self.cache.storage.delete(self.key_name(key))
|
||||||
|
|
||||||
|
|
||||||
|
class BaseCache(object):
|
||||||
|
|
||||||
|
def __new__(cls, *args, **kwargs):
|
||||||
|
self = super(BaseCache, cls).__new__(cls)
|
||||||
|
api_endpoints = inspect.getmembers(self, _is_cache_item)
|
||||||
|
for name, api in api_endpoints:
|
||||||
|
api_cls = type(api)
|
||||||
|
api = api_cls(self, name)
|
||||||
|
setattr(self, name, api)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __init__(self, storage, prefix='client'):
|
||||||
|
assert isinstance(storage, BaseStorage)
|
||||||
|
self.storage = storage
|
||||||
|
self.prefix = prefix
|
||||||
|
|
||||||
|
|
||||||
|
class WeWorkCache(BaseCache):
|
||||||
|
access_token = CacheItem()
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import absolute_import, unicode_literals
|
||||||
|
|
||||||
|
import json
|
||||||
|
|
||||||
|
from dingtalk.core.utils import to_text
|
||||||
|
from utils.storage import BaseStorage
|
||||||
|
|
||||||
|
|
||||||
|
class KvStorage(BaseStorage):
|
||||||
|
|
||||||
|
def __init__(self, kvdb, prefix='wework'):
|
||||||
|
for method_name in ('get', 'set', 'delete'):
|
||||||
|
assert hasattr(kvdb, method_name)
|
||||||
|
self.kvdb = kvdb
|
||||||
|
self.prefix = prefix
|
||||||
|
|
||||||
|
def key_name(self, key):
|
||||||
|
return '{0}:{1}'.format(self.prefix, key)
|
||||||
|
|
||||||
|
def get(self, key, default=None):
|
||||||
|
key = self.key_name(key)
|
||||||
|
value = self.kvdb.get(key)
|
||||||
|
if value is None:
|
||||||
|
return default
|
||||||
|
return json.loads(to_text(value))
|
||||||
|
|
||||||
|
def set(self, key, value, ttl=None):
|
||||||
|
if value is None:
|
||||||
|
return
|
||||||
|
key = self.key_name(key)
|
||||||
|
value = json.dumps(value)
|
||||||
|
self.kvdb.set(key, value, ttl)
|
||||||
|
|
||||||
|
def delete(self, key):
|
||||||
|
key = self.key_name(key)
|
||||||
|
self.kvdb.delete(key)
|
|
@ -0,0 +1,32 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import absolute_import, unicode_literals
|
||||||
|
|
||||||
|
import time
|
||||||
|
|
||||||
|
from utils.storage import BaseStorage
|
||||||
|
|
||||||
|
|
||||||
|
class MemoryStorage(BaseStorage):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self._data = {}
|
||||||
|
|
||||||
|
def get(self, key, default=None):
|
||||||
|
ret = self._data.get(key, None)
|
||||||
|
if ret is None or len(ret) != 2:
|
||||||
|
return default
|
||||||
|
else:
|
||||||
|
value = ret[0]
|
||||||
|
expires_at = ret[1]
|
||||||
|
if expires_at is None or expires_at > time.time():
|
||||||
|
return value
|
||||||
|
else:
|
||||||
|
return default
|
||||||
|
|
||||||
|
def set(self, key, value, ttl=None):
|
||||||
|
if value is None:
|
||||||
|
return
|
||||||
|
self._data[key] = (value, int(time.time()) + ttl)
|
||||||
|
|
||||||
|
def delete(self, key):
|
||||||
|
self._data.pop(key, None)
|
|
@ -0,0 +1,113 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding:utf-8 -*-
|
||||||
|
|
||||||
|
import json
|
||||||
|
|
||||||
|
import requests
|
||||||
|
|
||||||
|
DEBUG = False
|
||||||
|
|
||||||
|
|
||||||
|
class ApiException(Exception):
|
||||||
|
def __init__(self, errCode, errMsg):
|
||||||
|
self.errCode = errCode
|
||||||
|
self.errMsg = errMsg
|
||||||
|
|
||||||
|
|
||||||
|
class AbstractApi(object):
|
||||||
|
def __init__(self):
|
||||||
|
return
|
||||||
|
|
||||||
|
def access_token(self):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def http_call(self, url_type, args=None):
|
||||||
|
short_url = url_type[0]
|
||||||
|
method = url_type[1]
|
||||||
|
response = {}
|
||||||
|
for retryCnt in range(0, 3):
|
||||||
|
if 'POST' == method:
|
||||||
|
url = self.__make_url(short_url)
|
||||||
|
response = self.__http_post(url, args)
|
||||||
|
elif 'GET' == method:
|
||||||
|
url = self.__make_url(short_url)
|
||||||
|
url = self.__append_args(url, args)
|
||||||
|
response = self.__http_get(url)
|
||||||
|
else:
|
||||||
|
raise ApiException(-1, "unknown method type")
|
||||||
|
|
||||||
|
# check if token expired
|
||||||
|
if self.__token_expired(response.get('errcode')):
|
||||||
|
self.__refresh_token(short_url)
|
||||||
|
retryCnt += 1
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
|
return self.__check_response(response)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def __append_args(url, args):
|
||||||
|
if args is None:
|
||||||
|
return url
|
||||||
|
|
||||||
|
for key, value in args.items():
|
||||||
|
if '?' in url:
|
||||||
|
url += ('&' + key + '=' + value)
|
||||||
|
else:
|
||||||
|
url += ('?' + key + '=' + value)
|
||||||
|
return url
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def __make_url(short_url):
|
||||||
|
base = "https://qyapi.weixin.qq.com"
|
||||||
|
if short_url[0] == '/':
|
||||||
|
return base + short_url
|
||||||
|
else:
|
||||||
|
return base + '/' + short_url
|
||||||
|
|
||||||
|
def __append_token(self, url):
|
||||||
|
if 'ACCESS_TOKEN' in url:
|
||||||
|
return url.replace('ACCESS_TOKEN', self.access_token())
|
||||||
|
else:
|
||||||
|
return url
|
||||||
|
|
||||||
|
def __http_post(self, url, args):
|
||||||
|
real_url = self.__append_token(url)
|
||||||
|
|
||||||
|
if DEBUG is True:
|
||||||
|
print(real_url, args)
|
||||||
|
|
||||||
|
return requests.post(real_url, data=json.dumps(args, ensure_ascii=False).encode('utf-8')).json()
|
||||||
|
|
||||||
|
def __http_get(self, url):
|
||||||
|
real_url = self.__append_token(url)
|
||||||
|
|
||||||
|
if DEBUG is True:
|
||||||
|
print(real_url)
|
||||||
|
|
||||||
|
return requests.get(real_url).json()
|
||||||
|
|
||||||
|
def __post_file(self, url, media_file):
|
||||||
|
return requests.post(url, file=media_file).json()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def __check_response(response):
|
||||||
|
errCode = response.get('errcode')
|
||||||
|
errMsg = response.get('errmsg')
|
||||||
|
|
||||||
|
if errCode == 0:
|
||||||
|
return response
|
||||||
|
else:
|
||||||
|
raise ApiException(errCode, errMsg)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def __token_expired(errCode):
|
||||||
|
if errCode == 40014 or errCode == 42001 or errCode == 42007 or errCode == 42009:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def __refresh_token(self, url):
|
||||||
|
if 'ACCESS_TOKEN' in url:
|
||||||
|
self.access_token()
|
|
@ -0,0 +1,140 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# @FileName: WEWORK_ops.py
|
||||||
|
# @Software:
|
||||||
|
# @Author: Leven Xiang
|
||||||
|
# @Mail: xiangle0109@outlook.com
|
||||||
|
# @Date: 2021/5/18 16:55
|
||||||
|
from __future__ import absolute_import, unicode_literals
|
||||||
|
|
||||||
|
from pwdselfservice import cache_storage
|
||||||
|
|
||||||
|
from pwdselfservice.local_settings import *
|
||||||
|
from utils.storage.cache import WeWorkCache
|
||||||
|
from utils.wework_api.AbstractApi import *
|
||||||
|
|
||||||
|
CORP_API_TYPE = {
|
||||||
|
'GET_ACCESS_TOKEN': ['/cgi-bin/gettoken', 'GET'],
|
||||||
|
'USER_CREATE': ['/cgi-bin/user/create?access_token=ACCESS_TOKEN', 'POST'],
|
||||||
|
'USER_GET': ['/cgi-bin/user/get?access_token=ACCESS_TOKEN', 'GET'],
|
||||||
|
'USER_UPDATE': ['/cgi-bin/user/update?access_token=ACCESS_TOKEN', 'POST'],
|
||||||
|
'USER_DELETE': ['/cgi-bin/user/delete?access_token=ACCESS_TOKEN', 'GET'],
|
||||||
|
'USER_BATCH_DELETE': ['/cgi-bin/user/batchdelete?access_token=ACCESS_TOKEN', 'POST'],
|
||||||
|
'USER_SIMPLE_LIST': ['/cgi-bin/user/simplelist?access_token=ACCESS_TOKEN', 'GET'],
|
||||||
|
'USER_LIST': ['/cgi-bin/user/list?access_token=ACCESS_TOKEN', 'GET'],
|
||||||
|
'USERID_TO_OPENID': ['/cgi-bin/user/convert_to_openid?access_token=ACCESS_TOKEN', 'POST'],
|
||||||
|
'OPENID_TO_USERID': ['/cgi-bin/user/convert_to_userid?access_token=ACCESS_TOKEN', 'POST'],
|
||||||
|
'USER_AUTH_SUCCESS': ['/cgi-bin/user/authsucc?access_token=ACCESS_TOKEN', 'GET'],
|
||||||
|
|
||||||
|
'DEPARTMENT_CREATE': ['/cgi-bin/department/create?access_token=ACCESS_TOKEN', 'POST'],
|
||||||
|
'DEPARTMENT_UPDATE': ['/cgi-bin/department/update?access_token=ACCESS_TOKEN', 'POST'],
|
||||||
|
'DEPARTMENT_DELETE': ['/cgi-bin/department/delete?access_token=ACCESS_TOKEN', 'GET'],
|
||||||
|
'DEPARTMENT_LIST': ['/cgi-bin/department/list?access_token=ACCESS_TOKEN', 'GET'],
|
||||||
|
|
||||||
|
'TAG_CREATE': ['/cgi-bin/tag/create?access_token=ACCESS_TOKEN', 'POST'],
|
||||||
|
'TAG_UPDATE': ['/cgi-bin/tag/update?access_token=ACCESS_TOKEN', 'POST'],
|
||||||
|
'TAG_DELETE': ['/cgi-bin/tag/delete?access_token=ACCESS_TOKEN', 'GET'],
|
||||||
|
'TAG_GET_USER': ['/cgi-bin/tag/get?access_token=ACCESS_TOKEN', 'GET'],
|
||||||
|
'TAG_ADD_USER': ['/cgi-bin/tag/addtagusers?access_token=ACCESS_TOKEN', 'POST'],
|
||||||
|
'TAG_DELETE_USER': ['/cgi-bin/tag/deltagusers?access_token=ACCESS_TOKEN', 'POST'],
|
||||||
|
'TAG_GET_LIST': ['/cgi-bin/tag/list?access_token=ACCESS_TOKEN', 'GET'],
|
||||||
|
|
||||||
|
'BATCH_JOB_GET_RESULT': ['/cgi-bin/batch/getresult?access_token=ACCESS_TOKEN', 'GET'],
|
||||||
|
|
||||||
|
'BATCH_INVITE': ['/cgi-bin/batch/invite?access_token=ACCESS_TOKEN', 'POST'],
|
||||||
|
|
||||||
|
'AGENT_GET': ['/cgi-bin/agent/get?access_token=ACCESS_TOKEN', 'GET'],
|
||||||
|
'AGENT_SET': ['/cgi-bin/agent/set?access_token=ACCESS_TOKEN', 'POST'],
|
||||||
|
'AGENT_GET_LIST': ['/cgi-bin/agent/list?access_token=ACCESS_TOKEN', 'GET'],
|
||||||
|
|
||||||
|
'MENU_CREATE': ['/cgi-bin/menu/create?access_token=ACCESS_TOKEN', 'POST'],
|
||||||
|
'MENU_GET': ['/cgi-bin/menu/get?access_token=ACCESS_TOKEN', 'GET'],
|
||||||
|
'MENU_DELETE': ['/cgi-bin/menu/delete?access_token=ACCESS_TOKEN', 'GET'],
|
||||||
|
|
||||||
|
'MESSAGE_SEND': ['/cgi-bin/message/send?access_token=ACCESS_TOKEN', 'POST'],
|
||||||
|
'MESSAGE_REVOKE': ['/cgi-bin/message/revoke?access_token=ACCESS_TOKEN', 'POST'],
|
||||||
|
|
||||||
|
'MEDIA_GET': ['/cgi-bin/media/get?access_token=ACCESS_TOKEN', 'GET'],
|
||||||
|
|
||||||
|
'GET_USER_INFO_BY_CODE': ['/cgi-bin/user/getuserinfo?access_token=ACCESS_TOKEN', 'GET'],
|
||||||
|
'GET_USER_DETAIL': ['/cgi-bin/user/getuserdetail?access_token=ACCESS_TOKEN', 'POST'],
|
||||||
|
|
||||||
|
'GET_TICKET': ['/cgi-bin/ticket/get?access_token=ACCESS_TOKEN', 'GET'],
|
||||||
|
'GET_JSAPI_TICKET': ['/cgi-bin/get_jsapi_ticket?access_token=ACCESS_TOKEN', 'GET'],
|
||||||
|
|
||||||
|
'GET_CHECKIN_OPTION': ['/cgi-bin/checkin/getcheckinoption?access_token=ACCESS_TOKEN', 'POST'],
|
||||||
|
'GET_CHECKIN_DATA': ['/cgi-bin/checkin/getcheckindata?access_token=ACCESS_TOKEN', 'POST'],
|
||||||
|
'GET_APPROVAL_DATA': ['/cgi-bin/corp/getapprovaldata?access_token=ACCESS_TOKEN', 'POST'],
|
||||||
|
|
||||||
|
'GET_INVOICE_INFO': ['/cgi-bin/card/invoice/reimburse/getinvoiceinfo?access_token=ACCESS_TOKEN', 'POST'],
|
||||||
|
'UPDATE_INVOICE_STATUS':
|
||||||
|
['/cgi-bin/card/invoice/reimburse/updateinvoicestatus?access_token=ACCESS_TOKEN', 'POST'],
|
||||||
|
'BATCH_UPDATE_INVOICE_STATUS':
|
||||||
|
['/cgi-bin/card/invoice/reimburse/updatestatusbatch?access_token=ACCESS_TOKEN', 'POST'],
|
||||||
|
'BATCH_GET_INVOICE_INFO':
|
||||||
|
['/cgi-bin/card/invoice/reimburse/getinvoiceinfobatch?access_token=ACCESS_TOKEN', 'POST'],
|
||||||
|
|
||||||
|
'APP_CHAT_CREATE': ['/cgi-bin/appchat/create?access_token=ACCESS_TOKEN', 'POST'],
|
||||||
|
'APP_CHAT_GET': ['/cgi-bin/appchat/get?access_token=ACCESS_TOKEN', 'GET'],
|
||||||
|
'APP_CHAT_UPDATE': ['/cgi-bin/appchat/update?access_token=ACCESS_TOKEN', 'POST'],
|
||||||
|
'APP_CHAT_SEND': ['/cgi-bin/appchat/send?access_token=ACCESS_TOKEN', 'POST'],
|
||||||
|
|
||||||
|
'MINIPROGRAM_CODE_TO_SESSION_KEY': ['/cgi-bin/miniprogram/jscode2session?access_token=ACCESS_TOKEN', 'GET'],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class WeWorkOps(AbstractApi):
|
||||||
|
def __init__(self, corp_id=WEWORK_CORP_ID, agent_id=WEWORK_AGENT_ID, agent_secret=WEWORK_AGNET_SECRET, storage=cache_storage, prefix='wework'):
|
||||||
|
super().__init__()
|
||||||
|
self.corp_id = corp_id
|
||||||
|
self.agent_id = agent_id
|
||||||
|
self.agent_secret = agent_secret
|
||||||
|
self.storage = storage
|
||||||
|
self.cache = WeWorkCache(self.storage, "%s:%s" % (prefix, "corp_id:%s" % self.corp_id))
|
||||||
|
|
||||||
|
def access_token(self):
|
||||||
|
access_token = self.cache.access_token.get()
|
||||||
|
if access_token is None:
|
||||||
|
ret = self.get_access_token()
|
||||||
|
access_token = ret['access_token']
|
||||||
|
expires_in = ret.get('expires_in', 7200)
|
||||||
|
self.cache.access_token.set(value=access_token, ttl=expires_in)
|
||||||
|
return access_token
|
||||||
|
|
||||||
|
def get_access_token(self):
|
||||||
|
return self.http_call(
|
||||||
|
CORP_API_TYPE['GET_ACCESS_TOKEN'],
|
||||||
|
{
|
||||||
|
'corpid': self.corp_id,
|
||||||
|
'corpsecret': self.agent_secret,
|
||||||
|
})
|
||||||
|
|
||||||
|
def get_user_id_by_code(self, code):
|
||||||
|
try:
|
||||||
|
return True, self.http_call(
|
||||||
|
CORP_API_TYPE['GET_USER_INFO_BY_CODE'],
|
||||||
|
{
|
||||||
|
'code': code,
|
||||||
|
}).get('UserId')
|
||||||
|
except ApiException as e:
|
||||||
|
return False, "get_user_id_by_code: {}-{}" .format(e.errCode, e.errMsg)
|
||||||
|
except Exception as e:
|
||||||
|
return False, "get_user_id_by_code: {}".format(e)
|
||||||
|
|
||||||
|
def get_user_detail_by_user_id(self, user_id):
|
||||||
|
try:
|
||||||
|
return True, self.http_call(
|
||||||
|
CORP_API_TYPE['USER_GET'],
|
||||||
|
{
|
||||||
|
'userid': user_id,
|
||||||
|
})
|
||||||
|
except ApiException as e:
|
||||||
|
return False, "get_user_detail_by_user_id: {}-{}" .format(e.errCode, e.errMsg)
|
||||||
|
except Exception as e:
|
||||||
|
return False, "get_user_detail_by_user_id: {}".format(e)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
wx = WeWorkOps()
|
||||||
|
print(wx.get_user_detail_by_user_id('XiangLe'))
|
||||||
|
|
Loading…
Reference in New Issue