codosdk 1.0.0__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
websdk2/jwt_token.py ADDED
@@ -0,0 +1,135 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding:utf-8 -*-
3
+ import jwt, datetime, hashlib
4
+ from .configs import configs as my_configs
5
+
6
+
7
+ class AuthToken:
8
+ def __init__(self):
9
+ self.token_secret = my_configs.get('token_secret', '3AIiOq18i~H=WWTIGq4ODQyMzcsIdfghs')
10
+
11
+ def encode_auth_token(self, **kwargs):
12
+ """
13
+ 生成认证Token
14
+ :param user_id: string
15
+ :param username: string
16
+ :param nickname: string
17
+ :return: string
18
+ """
19
+ try:
20
+ exp_time = kwargs.get('exp_time', 1)
21
+ payload = {
22
+ 'exp': datetime.datetime.utcnow() + datetime.timedelta(days=int(exp_time), seconds=10),
23
+ 'nbf': datetime.datetime.utcnow() - datetime.timedelta(seconds=10),
24
+ 'iat': datetime.datetime.utcnow(),
25
+ 'iss': 'auth: ss',
26
+ 'sub': 'my token',
27
+ 'id': '15618718060',
28
+ 'data': {
29
+ 'user_id': kwargs.get('user_id', ''),
30
+ 'username': kwargs.get('username', ''),
31
+ 'nickname': kwargs.get('nickname', ''),
32
+ 'email': kwargs.get('email', ''),
33
+ 'is_superuser': kwargs.get('is_superuser', False)
34
+ }
35
+ }
36
+ return jwt.encode(
37
+ payload,
38
+ self.token_secret,
39
+ algorithm='HS256'
40
+ )
41
+
42
+ except Exception as e:
43
+ return e
44
+
45
+ def encode_auth_token_v2(self, **kwargs):
46
+ ### 支持到自定义小时
47
+ """
48
+ 生成认证Token
49
+ :param user_id: string
50
+ :param username: string
51
+ :param nickname: string
52
+ :param exp_time: int
53
+ :param exp_hour: int
54
+ :return: string
55
+ """
56
+ try:
57
+ exp_days = kwargs.get('exp_days', 1)
58
+ exp_hours = kwargs.get('exp_hours')
59
+ if exp_hours and isinstance(exp_hours, int) and exp_days == 1:
60
+ exp_time = datetime.datetime.utcnow() + datetime.timedelta(hours=int(exp_hours), seconds=30)
61
+ else:
62
+ exp_time = datetime.datetime.utcnow() + datetime.timedelta(days=int(exp_days), seconds=30)
63
+
64
+ payload = {
65
+ 'exp': exp_time,
66
+ 'nbf': datetime.datetime.utcnow() - datetime.timedelta(seconds=10),
67
+ 'iat': datetime.datetime.utcnow(),
68
+ 'iss': 'auth: ss',
69
+ 'sub': 'my token',
70
+ 'id': '15618718060',
71
+ 'data': {
72
+ 'user_id': kwargs.get('user_id', ''),
73
+ 'username': kwargs.get('username', ''),
74
+ 'nickname': kwargs.get('nickname', ''),
75
+ 'email': kwargs.get('email', ''),
76
+ 'is_superuser': kwargs.get('is_superuser', False)
77
+ }
78
+ }
79
+ return jwt.encode(
80
+ payload,
81
+ self.token_secret,
82
+ algorithm='HS256'
83
+ )
84
+
85
+ except Exception as e:
86
+ return e
87
+
88
+ def encode_mfa_token(self, **kwargs):
89
+ try:
90
+ exp_days = kwargs.get('exp_days', 1)
91
+ exp_hours = kwargs.get('exp_hours')
92
+
93
+ current_time = datetime.datetime.utcnow()
94
+ exp_time = (datetime.datetime.utcnow() + datetime.timedelta(hours=exp_hours) if exp_hours
95
+ else datetime.datetime.utcnow() + datetime.timedelta(days=exp_days)) + datetime.timedelta(
96
+ seconds=30)
97
+
98
+ payload = {
99
+ 'exp': exp_time,
100
+ 'nbf': current_time - datetime.timedelta(seconds=10),
101
+ 'iat': current_time,
102
+ 'data': {
103
+ 'user_id': kwargs.get('user_id', ''),
104
+ 'email': kwargs.get('email', '')
105
+ }
106
+ }
107
+
108
+ return jwt.encode(payload, self.token_secret, algorithm='HS256')
109
+
110
+ except Exception as e:
111
+ return str(e)
112
+
113
+ def decode_auth_token(self, auth_token):
114
+ """
115
+ 验证Token
116
+ :param auth_token:
117
+ :return: dict
118
+ """
119
+ try:
120
+ payload = jwt.decode(auth_token, self.token_secret, algorithms=['HS256'],
121
+ leeway=datetime.timedelta(seconds=10))
122
+ if 'data' in payload and 'user_id' in payload['data']:
123
+ return payload['data']
124
+ else:
125
+ raise jwt.InvalidTokenError
126
+ except jwt.ExpiredSignatureError:
127
+ return dict(status=-1, msg='Token过期')
128
+ except jwt.InvalidTokenError:
129
+ return dict(status=-2, msg='无效Token')
130
+
131
+
132
+ def gen_md5(pd):
133
+ m2 = hashlib.md5()
134
+ m2.update(pd.encode("utf-8"))
135
+ return m2.hexdigest()
websdk2/ldap.py ADDED
@@ -0,0 +1,117 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Contact : 191715030@qq.com
5
+ Author : shenshuo
6
+ Date : 2023/3/17
7
+ Desc : 对接LDAP登录认证
8
+ """
9
+ import json
10
+ from ldap3 import Server, Connection, ALL, SUBTREE, ServerPool
11
+
12
+
13
+ class LdapApi:
14
+ def __init__(self, ldap_server_host, ldap_admin_dn, ldap_admin_password, ldap_server_port=389, ldap_use_ssl=False):
15
+ self._ldap_admin_dn = ldap_admin_dn
16
+ self._ldap_admin_password = ldap_admin_password
17
+ # ldap_server_pool = ServerPool(["172.16.0.102",'172.16.0.103'])
18
+ use_ssl = True if ldap_use_ssl in ['y', 'yes', True] else False
19
+ self.ldap_server = Server(ldap_server_host, port=ldap_server_port, use_ssl=ldap_use_ssl)
20
+
21
+ def ldap_server_test(self):
22
+ try:
23
+ conn = Connection(self.ldap_server, user=self._ldap_admin_dn, password=self._ldap_admin_password,
24
+ check_names=True, lazy=False, raise_exceptions=False)
25
+ conn.open()
26
+ conn.bind()
27
+ return True
28
+ except Exception as e:
29
+ print("auth fail {}".format(e))
30
+ return False
31
+
32
+ def ldap_auth_v2(self, username, password, search_base, search_filter='cn'):
33
+ if not self.ldap_server_test(): return False, None, None
34
+
35
+ conn = Connection(self.ldap_server, user=self._ldap_admin_dn, password=self._ldap_admin_password,
36
+ check_names=True, lazy=False, raise_exceptions=False)
37
+ conn.open()
38
+ conn.bind()
39
+ res = conn.search(search_base=search_base, search_filter=f'({search_filter}={username})',
40
+ search_scope=SUBTREE, attributes=[search_filter, 'cn', 'sAMAccountName'], paged_size=5)
41
+ if not res: return False, None, None
42
+ entry = conn.response[0]
43
+ dn = entry['dn']
44
+ attr_dict = entry['attributes']
45
+
46
+ # check password by dn
47
+ try:
48
+ conn2 = Connection(self.ldap_server, user=dn, password=password, check_names=True, lazy=False,
49
+ raise_exceptions=False)
50
+ conn2.bind()
51
+ if conn2.result["description"] == "success":
52
+ try:
53
+ if 'email' in attr_dict and isinstance(attr_dict["email"], list) and attr_dict["email"]:
54
+ email = attr_dict["email"][0]
55
+ elif 'email' in attr_dict and not isinstance(attr_dict["email"], list) and attr_dict["email"]:
56
+ email = attr_dict["email"]
57
+ elif 'mail' in attr_dict and isinstance(attr_dict["mail"], list) and attr_dict["mail"]:
58
+ email = attr_dict["mail"][0]
59
+ elif 'mail' in attr_dict and not isinstance(attr_dict["mail"], list) and attr_dict["mail"]:
60
+ email = attr_dict["mail"]
61
+ else:
62
+ email = None
63
+ except Exception as err:
64
+ print(f"email fail, {err}")
65
+ email = None
66
+
67
+ return True, attr_dict[search_filter], email
68
+ else:
69
+ print("auth fail")
70
+ return False, None, None
71
+ except Exception as e:
72
+ print("auth fail {}".format(e))
73
+ return False, None, None
74
+
75
+ def ldap_auth_v3(self, username, password, search_base, conf_attr_dict, search_filter='cn'):
76
+ # 用户 密码 用户ou 映射数据 查询过滤 应和登录的账户关联
77
+ if not self.ldap_server_test():
78
+ return False, None
79
+
80
+ conn = Connection(self.ldap_server, user=self._ldap_admin_dn, password=self._ldap_admin_password,
81
+ check_names=True, lazy=False, raise_exceptions=False)
82
+ conn.open()
83
+ conn.bind()
84
+ try:
85
+ if isinstance(conf_attr_dict, str): conf_attr_dict = json.loads(conf_attr_dict)
86
+ attr_list = list(conf_attr_dict.values())
87
+ except Exception as err:
88
+ attr_list = ['cn', 'sAMAccountName']
89
+ res = conn.search(search_base=search_base, search_filter=f'({search_filter}={username})',
90
+ search_scope=SUBTREE, attributes=attr_list, paged_size=1000)
91
+
92
+ if not res:
93
+ return False, None
94
+ entry = conn.response[0]
95
+ dn = entry['dn']
96
+ attr_dict = entry['attributes']
97
+
98
+ # check password by dn
99
+ try:
100
+ conn2 = Connection(self.ldap_server, user=dn, password=password, check_names=True, lazy=False,
101
+ raise_exceptions=False)
102
+ conn2.bind()
103
+ if conn2.result["description"] == "success":
104
+ return True, {k: attr_dict.get(v) for k, v in conf_attr_dict.items()}
105
+ else:
106
+ print("auth fail 2")
107
+ return False, None
108
+ except Exception as e:
109
+ print(f"auth fail 3 {e}")
110
+ return False, None
111
+
112
+
113
+ if __name__ == "__main__":
114
+ obj = LdapApi('172.16.0.102', 'cn=Manager,DC=sz,DC=com', '070068')
115
+ print(obj.ldap_server_test())
116
+ print('____________')
117
+ print(obj.ldap_auth_v2("yanghongfei", "123456", 'ou=opendevops,dc=sz,dc=com', 'cn'))
websdk2/logger.py ADDED
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env python
2
+ # -*-coding:utf-8-*-
3
+
4
+ import os
5
+ import logging
6
+ import sys
7
+ import datetime
8
+ import tornado.log
9
+
10
+ # from tornado.options import options
11
+ #
12
+ # options.log_file_prefix = os.path.join(os.path.dirname(os.path.dirname(__file__)), f'/tmp/codo.log')
13
+
14
+
15
+ class LogFormatter(tornado.log.LogFormatter):
16
+ default_msec_format = '%s.%03d'
17
+
18
+ def __init__(self):
19
+ super(LogFormatter, self).__init__(
20
+ fmt='%(color)s%(asctime)s | %(levelname)s%(end_color)s | %(filename)s:%(funcName)s:%(lineno)s - %(message)s',
21
+ datefmt='%Y-%m-%d %H:%M:%S.%f'
22
+ )
23
+
24
+ def formatTime(self, record, datefmt=None):
25
+ ct = datetime.datetime.now()
26
+ t = ct.strftime(self.default_time_format)
27
+ s = self.default_msec_format % (t, record.msecs)
28
+ return s
29
+
30
+
31
+ def init_logging():
32
+ # write file
33
+ [
34
+ i.setFormatter(LogFormatter())
35
+ for i in logging.getLogger().handlers
36
+ ]
37
+ logging.getLogger().setLevel(logging.INFO)
38
+ # write stdout
39
+ stdout_handler = logging.StreamHandler(sys.stdout)
40
+ stdout_handler.setFormatter(LogFormatter())
41
+ logging.getLogger().addHandler(stdout_handler)
42
+ logging.info('[APP Logging Init] logging has been started')
websdk2/model_utils.py ADDED
@@ -0,0 +1,156 @@
1
+ #!/usr/bin/env python
2
+ # -*-coding:utf-8-*-
3
+ """
4
+ Author : shenshuo
5
+ Date : 2019年12月11日
6
+ Desc : models类
7
+ """
8
+
9
+ from typing import Type, Union
10
+ from datetime import datetime
11
+ from sqlalchemy.orm import class_mapper
12
+ from sqlalchemy.ext.declarative import DeclarativeMeta
13
+ from sqlalchemy import text
14
+ from sqlalchemy.exc import IntegrityError
15
+ from .utils import get_contain_dict
16
+ from .db_context import DBContextV2 as DBContext
17
+ from .utils.pydantic_utils import sqlalchemy_to_pydantic, ValidationError, PydanticDelList
18
+
19
+
20
+ def model_to_dict(model):
21
+ model_dict = {}
22
+ for key, column in class_mapper(model.__class__).c.items():
23
+ if isinstance(getattr(model, key), datetime):
24
+ model_dict[column.name] = str(getattr(model, key))
25
+ else:
26
+ model_dict[column.name] = getattr(model, key, None)
27
+
28
+ if isinstance(getattr(model, "custom_extend_column_dict", None), dict):
29
+ model_dict.update(**getattr(model, "custom_extend_column_dict", {}))
30
+ return model_dict
31
+
32
+
33
+ def queryset_to_list(queryset, **kwargs) -> list:
34
+ if kwargs: return [model_to_dict(q) for q in queryset if get_contain_dict(kwargs, model_to_dict(q))]
35
+ return [model_to_dict(q) for q in queryset]
36
+
37
+
38
+ def GetInsertOrUpdateObj(cls: Type, str_filter: str, **kw) -> classmethod:
39
+ """
40
+ cls: Model 类名
41
+ str_filter: filter的参数.eg:"name='name-14'" 必须设置唯一 支持 and or
42
+ **kw: 【属性、值】字典,用于构建新实例,或修改存在的记录
43
+ session.add(GetInsertOrUpdateObj(TableTest, "name='name-114'", age=33114, height=123.14, name='name-114'))
44
+ """
45
+ with DBContext('r') as session:
46
+ existing = session.query(cls).filter(text(str_filter)).first()
47
+ if not existing:
48
+ res = cls()
49
+ for k, v in kw.items():
50
+ if hasattr(res, k):
51
+ setattr(res, k, v)
52
+ return res
53
+ else:
54
+ res = existing
55
+ for k, v in kw.items():
56
+ if hasattr(res, k):
57
+ setattr(res, k, v)
58
+
59
+ return res
60
+
61
+
62
+ def insert_or_update(cls: Type[DeclarativeMeta], str_filter: str, **kwargs) -> Union[None, DeclarativeMeta]:
63
+ """
64
+ Insert or update a record in the database.
65
+
66
+ Args:
67
+ cls (Type[DeclarativeMeta]): Model class.
68
+ str_filter (str): Filter parameters. e.g., "name='name-14'". Must be unique. Supports 'and' and 'or'.
69
+ **kwargs: Attributes and values dictionary used to construct a new instance or modify an existing record.
70
+
71
+ Returns:
72
+ Union[None, DeclarativeMeta]: Returns None if no existing record found, otherwise returns the updated or inserted record.
73
+ """
74
+ with DBContext('r') as session:
75
+ existing = session.query(cls).filter(text(str_filter)).first()
76
+ if not existing:
77
+ res = cls(**kwargs)
78
+ for k, v in kwargs.items():
79
+ if hasattr(res, k):
80
+ setattr(res, k, v)
81
+ return res
82
+ else:
83
+ res = existing
84
+ for k, v in kwargs.items():
85
+ if hasattr(res, k):
86
+ setattr(res, k, v)
87
+
88
+ return res
89
+
90
+
91
+ class CommonOptView:
92
+ def __init__(self, model, **kwargs):
93
+ self.model = model
94
+ self.pydantic_model_base = sqlalchemy_to_pydantic(model)
95
+ self.pydantic_model = sqlalchemy_to_pydantic(model, exclude=['id'])
96
+
97
+ def prepare(self):
98
+ pass
99
+
100
+ @staticmethod
101
+ def del_data(data) -> dict:
102
+ if '_index' in data:
103
+ del data['_index']
104
+ if '_rowKey' in data:
105
+ del data['_rowKey']
106
+ return data
107
+
108
+ def handle_add(self, data: dict) -> dict:
109
+ self.prepare()
110
+ data = self.del_data(data)
111
+ try:
112
+ self.pydantic_model(**data)
113
+ except ValidationError as e:
114
+ return dict(code=-1, msg=str(e))
115
+
116
+ try:
117
+ with DBContext('w', None, True) as db:
118
+ db.add(self.model(**data))
119
+ except IntegrityError as e:
120
+ return dict(code=-2, msg='不要重复添加')
121
+
122
+ except Exception as e:
123
+ return dict(code=-3, msg=f'{e}')
124
+
125
+ return dict(code=0, msg="创建成功")
126
+
127
+ def handle_update(self, data: dict) -> dict:
128
+ self.prepare()
129
+ data = self.del_data(data)
130
+ try:
131
+ valid_data = self.pydantic_model_base(**data)
132
+ except ValidationError as e:
133
+ return dict(code=-1, msg=str(e))
134
+
135
+ try:
136
+ with DBContext('w', None, True) as db:
137
+ db.query(self.model).filter(self.model.id == valid_data.id).update(data)
138
+
139
+ except IntegrityError as e:
140
+ return dict(code=-2, msg=f'修改失败,已存在')
141
+
142
+ except Exception as err:
143
+ return dict(code=-3, msg=f'修改失败, {err}')
144
+
145
+ return dict(code=0, msg="修改成功")
146
+
147
+ def handle_delete(self, data: dict) -> dict:
148
+ self.prepare()
149
+ try:
150
+ valid_data = PydanticDelList(**data)
151
+ except ValidationError as e:
152
+ return dict(code=-1, msg=str(e))
153
+
154
+ with DBContext('w', None, True) as session:
155
+ session.query(self.model).filter(self.model.id.in_(valid_data.id_list)).delete(synchronize_session=False)
156
+ return dict(code=0, msg=f"删除成功")
websdk2/mqhelper.py ADDED
@@ -0,0 +1,120 @@
1
+ # !/usr/bin/env python
2
+ # -*-coding:utf-8-*-
3
+ """
4
+ Author : ming
5
+ date : 2017/3/3 下午9:31
6
+ role : rabbitMQ 操作类
7
+ """
8
+
9
+ import logging
10
+ import traceback
11
+ import pika
12
+ from .consts import const
13
+ from .configs import configs
14
+ from .error import ConfigError
15
+
16
+ logger = logging.getLogger('pika')
17
+ logger.setLevel(logging.WARNING)
18
+
19
+
20
+ class MessageQueueBase(object):
21
+ def __init__(self, exchange, exchange_type, routing_key='', routing_keys=None, queue_name='', no_ack=False,
22
+ mq_key=const.DEFAULT_MQ_KEY):
23
+ mq_config = configs[const.MQ_CONFIG_ITEM][mq_key]
24
+ if const.MQ_ADDR not in mq_config:
25
+ raise ConfigError(const.MQ_ADDR)
26
+ if const.MQ_PORT not in mq_config:
27
+ raise ConfigError(const.MQ_PORT)
28
+ if const.MQ_VHOST not in mq_config:
29
+ raise ConfigError(const.MQ_VHOST)
30
+ if const.MQ_USER not in mq_config:
31
+ raise ConfigError(const.MQ_USER)
32
+ if const.MQ_PWD not in mq_config:
33
+ raise ConfigError(const.MQ_PWD)
34
+ self.addr = mq_config[const.MQ_ADDR]
35
+ self.port = int(mq_config[const.MQ_PORT])
36
+ self.vhost = mq_config[const.MQ_VHOST]
37
+ self.user = mq_config[const.MQ_USER]
38
+ self.pwd = mq_config[const.MQ_PWD]
39
+ self.__exchange = exchange
40
+ self.__exchange_type = exchange_type
41
+ self.__routing_key = routing_key
42
+ self.__routing_keys = routing_keys
43
+ self.__queue_name = queue_name
44
+ self.__no_ack = no_ack
45
+ self.__channel = None
46
+ self.__connection = None
47
+
48
+ def start_consuming(self, exchange_durable=False):
49
+ channel = self.create_channel()
50
+
51
+ channel.exchange_declare(exchange=self.__exchange, exchange_type=self.__exchange_type, durable=exchange_durable)
52
+ if self.__queue_name:
53
+ result = channel.queue_declare(queue=self.__queue_name, durable=True)
54
+ else:
55
+ result = channel.queue_declare('', exclusive=True)
56
+ if self.__routing_keys and isinstance(self.__routing_keys, list):
57
+ for binding_key in self.__routing_keys:
58
+ channel.queue_bind(exchange=self.__exchange, queue=result.method.queue, routing_key=binding_key)
59
+ else:
60
+ channel.queue_bind(exchange=self.__exchange, queue=result.method.queue, routing_key=self.__routing_key)
61
+
62
+ channel.basic_qos(prefetch_count=1)
63
+ channel.basic_consume(result.method.queue, self.call_back, self.__no_ack)
64
+ logging.info('[*]Queue %s started.' % (result.method.queue))
65
+
66
+ channel.start_consuming()
67
+
68
+ def __enter__(self):
69
+ self.__channel = self.create_channel()
70
+ return self
71
+
72
+ def __exit__(self, exc_type, exc_val, exc_tb):
73
+ self.__connection.close()
74
+
75
+ def close_channel(self):
76
+ self.__connection.close()
77
+
78
+ def create_channel(self):
79
+ credentials = pika.PlainCredentials(self.user, self.pwd)
80
+ self.__connection = pika.BlockingConnection(
81
+ pika.ConnectionParameters(self.addr, self.port, self.vhost, credentials=credentials))
82
+ channel = self.__connection.channel()
83
+ return channel
84
+
85
+ def new_channel(self):
86
+ self.__channel = self.create_channel()
87
+ return self
88
+
89
+ def call_back(self, ch, method, properties, body):
90
+ try:
91
+ logging.info('get message')
92
+ self.on_message(body)
93
+
94
+ if not self.__no_ack:
95
+ ch.basic_ack(delivery_tag=method.delivery_tag)
96
+ except:
97
+ logging.error(traceback.format_exc())
98
+ if not self.__no_ack:
99
+ ch.basic_nack(delivery_tag=method.delivery_tag)
100
+
101
+ def on_message(self, body):
102
+ pass
103
+
104
+ def publish_message(self, body, durable=True, exchange_durable=False):
105
+ self.__channel.exchange_declare(exchange=self.__exchange, exchange_type=self.__exchange_type,
106
+ durable=exchange_durable)
107
+ if self.__queue_name:
108
+ result = self.__channel.queue_declare(queue=self.__queue_name)
109
+ else:
110
+ result = self.__channel.queue_declare("", exclusive=True, auto_delete=True)
111
+
112
+ self.__channel.queue_bind(exchange=self.__exchange, queue=result.method.queue)
113
+
114
+ if durable:
115
+ properties = pika.BasicProperties(delivery_mode=2)
116
+ self.__channel.basic_publish(exchange=self.__exchange, routing_key=self.__routing_key, body=body,
117
+ properties=properties)
118
+ else:
119
+ self.__channel.basic_publish(exchange=self.__exchange, routing_key=self.__routing_key, body=body)
120
+ logging.info('Publish message %s sucessfuled.' % body)
websdk2/program.py ADDED
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env python
2
+ # -*-coding:utf-8-*-
3
+ '''
4
+ Author : ming
5
+ date : 2017/4/11 下午3:21
6
+ desc :
7
+ '''
8
+ import fire
9
+
10
+
11
+ class MainProgram(object):
12
+ def __init__(self, progressid=''):
13
+ print(progressid)
14
+
15
+ @staticmethod
16
+ def run(cls_inst):
17
+ if issubclass(cls_inst, MainProgram):
18
+ fire.Fire(cls_inst)
19
+ else:
20
+ raise Exception('')