cmdbox 0.6.2.1__py3-none-any.whl → 0.6.2.3__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.

Potentially problematic release.


This version of cmdbox might be problematic. Click here for more details.

Files changed (37) hide show
  1. cmdbox/app/auth/signin.py +3 -3
  2. cmdbox/app/common.py +21 -15
  3. cmdbox/app/feature.py +5 -1
  4. cmdbox/app/features/cli/agent_base.py +1 -1
  5. cmdbox/app/features/cli/cmdbox_audit_search.py +17 -1
  6. cmdbox/app/features/cli/cmdbox_audit_write.py +260 -260
  7. cmdbox/app/features/cli/cmdbox_cmd_list.py +2 -2
  8. cmdbox/app/features/cli/cmdbox_cmd_load.py +2 -2
  9. cmdbox/app/features/cli/cmdbox_edge_config.py +2 -2
  10. cmdbox/app/features/cli/cmdbox_edge_start.py +1 -1
  11. cmdbox/app/features/cli/cmdbox_mcp_client.py +174 -0
  12. cmdbox/app/features/cli/cmdbox_mcp_proxy.py +2 -2
  13. cmdbox/app/features/cli/cmdbox_server_start.py +1 -1
  14. cmdbox/app/features/cli/cmdbox_web_apikey_add.py +2 -2
  15. cmdbox/app/features/cli/cmdbox_web_apikey_del.py +2 -2
  16. cmdbox/app/features/cli/cmdbox_web_gencert.py +3 -3
  17. cmdbox/app/features/cli/cmdbox_web_group_add.py +2 -2
  18. cmdbox/app/features/cli/cmdbox_web_group_del.py +2 -2
  19. cmdbox/app/features/cli/cmdbox_web_group_edit.py +2 -2
  20. cmdbox/app/features/cli/cmdbox_web_group_list.py +2 -2
  21. cmdbox/app/features/cli/cmdbox_web_start.py +11 -11
  22. cmdbox/app/features/cli/cmdbox_web_stop.py +1 -1
  23. cmdbox/app/features/cli/cmdbox_web_user_add.py +2 -2
  24. cmdbox/app/features/cli/cmdbox_web_user_del.py +2 -2
  25. cmdbox/app/features/cli/cmdbox_web_user_edit.py +2 -2
  26. cmdbox/app/features/cli/cmdbox_web_user_list.py +2 -2
  27. cmdbox/app/options.py +2 -3
  28. cmdbox/extensions/features.yml +2 -14
  29. cmdbox/version.py +4 -3
  30. cmdbox/web/assets/cmdbox/common.js +2 -0
  31. cmdbox/web/assets/cmdbox/filer_modal.js +4 -2
  32. {cmdbox-0.6.2.1.dist-info → cmdbox-0.6.2.3.dist-info}/METADATA +1 -1
  33. {cmdbox-0.6.2.1.dist-info → cmdbox-0.6.2.3.dist-info}/RECORD +37 -36
  34. {cmdbox-0.6.2.1.dist-info → cmdbox-0.6.2.3.dist-info}/LICENSE +0 -0
  35. {cmdbox-0.6.2.1.dist-info → cmdbox-0.6.2.3.dist-info}/WHEEL +0 -0
  36. {cmdbox-0.6.2.1.dist-info → cmdbox-0.6.2.3.dist-info}/entry_points.txt +0 -0
  37. {cmdbox-0.6.2.1.dist-info → cmdbox-0.6.2.3.dist-info}/top_level.txt +0 -0
@@ -1,260 +1,260 @@
1
- from cmdbox.app import common, client
2
- from cmdbox.app.commons import convert, redis_client
3
- from cmdbox.app.features.cli import audit_base
4
- from cmdbox.app.options import Options
5
- from datetime import datetime
6
- from pathlib import Path
7
- from typing import Dict, Any, Tuple, List, Union
8
- import argparse
9
- import logging
10
- import json
11
- import uuid
12
-
13
-
14
- class AuditWrite(audit_base.AuditBase):
15
- def get_mode(self) -> Union[str, List[str]]:
16
- """
17
- この機能のモードを返します
18
-
19
- Returns:
20
- Union[str, List[str]]: モード
21
- """
22
- return 'audit'
23
-
24
- def get_cmd(self):
25
- """
26
- この機能のコマンドを返します
27
-
28
- Returns:
29
- str: コマンド
30
- """
31
- return 'write'
32
-
33
- def get_option(self):
34
- """
35
- この機能のオプションを返します
36
-
37
- Returns:
38
- Dict[str, Any]: オプション
39
- """
40
- opt = super().get_option()
41
- opt['description_ja'] = "監査を記録します。"
42
- opt['description_en'] = "Record the audit."
43
- opt['choice'] += [
44
- dict(opt="client_only", type=Options.T_BOOL, default=False, required=False, multi=False, hide=True, choice=[True, False],
45
- description_ja="サーバーへの接続を行わないようにします。",
46
- description_en="Do not make connections to the server."),
47
- dict(opt="audit_type", type=Options.T_STR, default=None, required=True, multi=False, hide=False, choice=Options.AUDITS,
48
- description_ja="監査の種類を指定します。",
49
- description_en="Specifies the audit type."),
50
- dict(opt="clmsg_id", type=Options.T_STR, default=None, required=False, multi=False, hide=False, choice=None,
51
- description_ja="クライアントのメッセージIDを指定します。省略した場合はuuid4で生成されます。",
52
- description_en="Specifies the message ID of the client. If omitted, uuid4 will be generated."),
53
- dict(opt="clmsg_date", type=Options.T_DATETIME, default=None, required=False, multi=False, hide=False, choice=None,
54
- description_ja="クライアントのメッセージ発生日時を指定します。省略した場合はサーバーの現在日時が使用されます。",
55
- description_en="Specifies the date and time the client message occurred. If omitted, the server's current date/time is used."),
56
- dict(opt="clmsg_src", type=Options.T_STR, default=None, required=False, multi=False, hide=False, choice=None,
57
- description_ja="クライアントのメッセージの発生源を指定します。通常 `cmdbox.app.feature.Feature` を継承したクラス名を指定します。",
58
- description_en="Specifies the source of client messages. Usually specifies the name of a class that extends `cmdbox.app.feature.Feature` ."),
59
- dict(opt="clmsg_title", type=Options.T_STR, default=None, required=False, multi=False, hide=False, choice=None,
60
- description_ja="クライアントのメッセージタイトルを指定します。通常コマンドタイトルを指定します。",
61
- description_en="Specifies the client message title. Usually specifies the command title."),
62
- dict(opt="clmsg_user", type=Options.T_STR, default=None, required=False, multi=False, hide=False, choice=None,
63
- description_ja="クライアントのメッセージを発生させたユーザーを指定します。",
64
- description_en="SpecSpecifies the user who generated the client message."),
65
- dict(opt="clmsg_body", type=Options.T_DICT, default=None, required=False, multi=True, hide=False, choice=None,
66
- description_ja="クライアントのメッセージの本文を辞書形式で指定します。",
67
- description_en="Specifies the body of the client's message in dictionary format."),
68
- dict(opt="clmsg_tag", type=Options.T_STR, default=None, required=False, multi=True, hide=False, choice=None,
69
- description_ja="クライアントのメッセージのタグを指定します。後で検索しやすくするために指定します。",
70
- description_en="Specifies the tag for the client's message. Specify to make it easier to search later."),
71
- dict(opt="retention_period_days", type=Options.T_INT, default=365, required=False, multi=False, hide=True, choice=None, web="mask",
72
- description_ja="監査を保存する日数を指定します。この日数より古い監査は削除します。0以下を指定すると無期限で保存されます。",
73
- description_en="Specify the number of days to keep the audit. If the number is less than or equal to 0, the audit will be kept indefinitely."),
74
- ]
75
- return opt
76
-
77
- def get_svcmd(self):
78
- """
79
- この機能のサーバー側のコマンドを返します
80
-
81
- Returns:
82
- str: サーバー側のコマンド
83
- """
84
- return 'audit_write'
85
-
86
- def apprun(self, logger:logging.Logger, args:argparse.Namespace, tm:float, pf:List[Dict[str, float]]=[]) -> Tuple[int, Dict[str, Any], Any]:
87
- """
88
- この機能の実行を行います
89
-
90
- Args:
91
- logger (logging.Logger): ロガー
92
- args (argparse.Namespace): 引数
93
- tm (float): 実行開始時間
94
- pf (List[Dict[str, float]]): 呼出元のパフォーマンス情報
95
-
96
- Returns:
97
- Tuple[int, Dict[str, Any], Any]: 終了コード, 結果, オブジェクト
98
- """
99
- if args.svname is None:
100
- msg = dict(warn=f"Please specify the --svname option.")
101
- common.print_format(msg, False, tm, args.output_json, args.output_json_append, pf=pf)
102
- return 1, msg, None
103
- if args.audit_type is None:
104
- msg = dict(warn=f"Please specify the --audit_type option.")
105
- common.print_format(msg, False, tm, args.output_json, args.output_json_append, pf=pf)
106
- return 1, msg, None
107
- if args.clmsg_id is None:
108
- args.clmsg_id = str(uuid.uuid4())
109
- if args.clmsg_date is None:
110
- args.clmsg_date = datetime.now().strftime('%Y-%m-%d %H:%M:%S') + common.get_tzoffset_str()
111
- if hasattr(args, 'client_only') and args.client_only==True:
112
- # クライアントのみの場合は、サーバーへの接続を行わない
113
- logger.warning(f"client_only is True. Not connecting to server. Skip writing the audit log.")
114
- ret = dict(success={k:v for k, v in vars(args).items() if v})
115
- common.print_format(ret, False, tm, args.output_json, args.output_json_append, pf=pf)
116
- return 0, ret, None
117
-
118
- audit_type_b64 = convert.str2b64str(args.audit_type)
119
- clmsg_id_b64 = convert.str2b64str(args.clmsg_id)
120
- clmsg_date_b64 = convert.str2b64str(args.clmsg_date)
121
- clmsg_src_b64 = convert.str2b64str(args.clmsg_src) if args.clmsg_src is not None else ''
122
- clmsg_title_b64 = convert.str2b64str(args.clmsg_title) if args.clmsg_title is not None else ''
123
- clmsg_user_b64 = convert.str2b64str(args.clmsg_user) if args.clmsg_user is not None else ''
124
- clmsg_body_str = json.dumps(args.clmsg_body, default=common.default_json_enc, ensure_ascii=False) if args.clmsg_body is not None else '{}'
125
- clmsg_body_b64 = convert.str2b64str(clmsg_body_str)
126
- clmsg_tag_str = json.dumps(args.clmsg_tag, default=common.default_json_enc, ensure_ascii=False) if args.clmsg_tag is not None else '[]'
127
- clmsg_tag_b64 = convert.str2b64str(clmsg_tag_str)
128
- pg_enabled = args.pg_enabled
129
- pg_host_b64 = convert.str2b64str(args.pg_host)
130
- pg_port = args.pg_port if isinstance(args.pg_port, int) else None
131
- pg_user_b64 = convert.str2b64str(args.pg_user)
132
- pg_password_b64 = convert.str2b64str(args.pg_password)
133
- pg_dbname_b64 = convert.str2b64str(args.pg_dbname)
134
-
135
- if logger.level == logging.DEBUG:
136
- logger.debug(f"audit write args: {args}")
137
- cl = client.Client(logger, redis_host=args.host, redis_port=args.port, redis_password=args.password, svname=args.svname)
138
- cl.redis_cli.send_cmd(self.get_svcmd(),
139
- [audit_type_b64, clmsg_id_b64, clmsg_date_b64, clmsg_src_b64, clmsg_title_b64, clmsg_user_b64, clmsg_body_b64, clmsg_tag_b64,
140
- pg_enabled, pg_host_b64, pg_port, pg_user_b64, pg_password_b64, pg_dbname_b64,
141
- args.retention_period_days],
142
- retry_count=args.retry_count, retry_interval=args.retry_interval, timeout=args.timeout, nowait=True)
143
- ret = dict(success=True)
144
- #common.print_format(ret, False, tm, None, False, pf=pf)
145
- return 0, ret, cl
146
-
147
- def is_cluster_redirect(self):
148
- """
149
- クラスター宛のメッセージの場合、メッセージを転送するかどうかを返します
150
-
151
- Returns:
152
- bool: メッセージを転送する場合はTrue
153
- """
154
- return False
155
-
156
- def svrun(self, data_dir:Path, logger:logging.Logger, redis_cli:redis_client.RedisClient, msg:List[str],
157
- sessions:Dict[str, Dict[str, Any]]) -> int:
158
- """
159
- この機能のサーバー側の実行を行います
160
-
161
- Args:
162
- data_dir (Path): データディレクトリ
163
- logger (logging.Logger): ロガー
164
- redis_cli (redis_client.RedisClient): Redisクライアント
165
- msg (List[str]): 受信メッセージ
166
- sessions (Dict[str, Dict[str, Any]]): セッション情報
167
-
168
- Returns:
169
- int: 終了コード
170
- """
171
- if logger.level == logging.DEBUG:
172
- logger.debug(f"audit write svrun msg: {msg}")
173
- audit_type = convert.b64str2str(msg[2])
174
- clmsg_id = convert.b64str2str(msg[3])
175
- clmsg_date = convert.b64str2str(msg[4])
176
- clmsg_src = convert.b64str2str(msg[5])
177
- clmsg_title = convert.b64str2str(msg[6])
178
- clmsg_user = convert.b64str2str(msg[7])
179
- clmsg_body = convert.b64str2str(msg[8])
180
- clmsg_tags = convert.b64str2str(msg[9])
181
- pg_enabled = True if msg[10]=='True' else False
182
- pg_host = convert.b64str2str(msg[11])
183
- pg_port = int(msg[12]) if msg[12]!='None' else None
184
- pg_user = convert.b64str2str(msg[13])
185
- pg_password = convert.b64str2str(msg[14])
186
- pg_dbname = convert.b64str2str(msg[15])
187
- retention_period_days = int(msg[16]) if msg[16] != 'None' else None
188
- svmsg_id = str(uuid.uuid4())
189
- st = self.write(msg[1], audit_type, clmsg_id, clmsg_date, clmsg_src, clmsg_title, clmsg_user, clmsg_body, clmsg_tags, svmsg_id,
190
- pg_enabled, pg_host, pg_port, pg_user, pg_password, pg_dbname,
191
- retention_period_days,
192
- data_dir, logger, redis_cli)
193
- return st
194
-
195
- def write(self, reskey:str, audit_type:str, clmsg_id:str, clmsg_date:str, clmsg_src:str, clmsg_title:str,
196
- clmsg_user:str, clmsg_body:str, clmsg_tags:str, svmsg_id:str,
197
- pg_enabled:bool, pg_host:str, pg_port:int, pg_user:str, pg_password:str, pg_dbname:str,
198
- retention_period_days:int,
199
- data_dir:Path, logger:logging.Logger, redis_cli:redis_client.RedisClient) -> int:
200
- """
201
- 監査ログを書き込む
202
-
203
- Args:
204
- reskey (str): レスポンスキー
205
- audit_type (str): 監査の種類
206
- clmsg_id (str): クライアントメッセージID
207
- clmsg_date (str): クライアントメッセージ発生日時
208
- clmsg_src (str): クライアントメッセージの発生源
209
- clmsg_title (str): クライアントメッセージのタイトル
210
- clmsg_user (str): クライアントメッセージの発生させたユーザー
211
- clmsg_body (str): クライアントメッセージの本文
212
- clmsg_tags (str): クライアントメッセージのタグ
213
- svmsg_id (str): サーバーメッセージID
214
- pg_enabled (bool): PostgreSQLを使用する場合はTrue
215
- pg_host (str): PostgreSQLホスト
216
- pg_port (int): PostgreSQLポート
217
- pg_user (str): PostgreSQLユーザー
218
- pg_password (str): PostgreSQLパスワード
219
- pg_dbname (str): PostgreSQLデータベース名
220
- retention_period_days (int): 監査を保存する日数
221
- data_dir (Path): データディレクトリ
222
- logger (logging.Logger): ロガー
223
- redis_cli (redis_client.RedisClient): Redisクライアント
224
-
225
- Returns:
226
- int: レスポンスコード
227
- """
228
- try:
229
- with self.initdb(data_dir, logger, pg_enabled, pg_host, pg_port, pg_user, pg_password, pg_dbname) as conn:
230
- cursor = conn.cursor()
231
- try:
232
- svmsg_date = datetime.now().strftime('%Y-%m-%d %H:%M:%S') + common.get_tzoffset_str()
233
- if not pg_enabled:
234
- cursor.execute('''
235
- INSERT INTO audit (audit_type, clmsg_id, clmsg_date, clmsg_src, clmsg_title, clmsg_user, clmsg_body, clmsg_tag,
236
- svmsg_id, svmsg_date)
237
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
238
- ''', (audit_type, clmsg_id, clmsg_date, clmsg_src, clmsg_title, clmsg_user, clmsg_body, clmsg_tags, svmsg_id, svmsg_date))
239
- if retention_period_days is not None and retention_period_days > 0:
240
- cursor.execute('DELETE FROM audit WHERE svmsg_date < datetime(CURRENT_TIMESTAMP, ?)',
241
- (f'-{retention_period_days} days',))
242
- else:
243
- cursor.execute('''
244
- INSERT INTO audit (audit_type, clmsg_id, clmsg_date, clmsg_src, clmsg_title, clmsg_user, clmsg_body, clmsg_tag,
245
- svmsg_id, svmsg_date)
246
- VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
247
- ''', (audit_type, clmsg_id, clmsg_date, clmsg_src, clmsg_title, clmsg_user, clmsg_body, clmsg_tags, svmsg_id, svmsg_date))
248
- if retention_period_days is not None and retention_period_days > 0:
249
- cursor.execute("DELETE FROM audit WHERE svmsg_date < CURRENT_TIMESTAMP + %s ",
250
- (f'-{retention_period_days} day',))
251
- conn.commit()
252
- rescode, msg = (self.RESP_SCCESS, dict(success=True))
253
- redis_cli.rpush(reskey, msg)
254
- return rescode
255
- finally:
256
- cursor.close()
257
- except Exception as e:
258
- logger.warning(f"Failed to write: {e}", exc_info=True)
259
- redis_cli.rpush(reskey, dict(warn=f"Failed to write: {e}"))
260
- return self.RESP_WARN
1
+ from cmdbox.app import common, client
2
+ from cmdbox.app.commons import convert, redis_client
3
+ from cmdbox.app.features.cli import audit_base
4
+ from cmdbox.app.options import Options
5
+ from datetime import datetime
6
+ from pathlib import Path
7
+ from typing import Dict, Any, Tuple, List, Union
8
+ import argparse
9
+ import logging
10
+ import json
11
+ import uuid
12
+
13
+
14
+ class AuditWrite(audit_base.AuditBase):
15
+ def get_mode(self) -> Union[str, List[str]]:
16
+ """
17
+ この機能のモードを返します
18
+
19
+ Returns:
20
+ Union[str, List[str]]: モード
21
+ """
22
+ return 'audit'
23
+
24
+ def get_cmd(self):
25
+ """
26
+ この機能のコマンドを返します
27
+
28
+ Returns:
29
+ str: コマンド
30
+ """
31
+ return 'write'
32
+
33
+ def get_option(self):
34
+ """
35
+ この機能のオプションを返します
36
+
37
+ Returns:
38
+ Dict[str, Any]: オプション
39
+ """
40
+ opt = super().get_option()
41
+ opt['description_ja'] = "監査を記録します。"
42
+ opt['description_en'] = "Record the audit."
43
+ opt['choice'] += [
44
+ dict(opt="client_only", type=Options.T_BOOL, default=False, required=False, multi=False, hide=True, choice=[True, False],
45
+ description_ja="サーバーへの接続を行わないようにします。",
46
+ description_en="Do not make connections to the server."),
47
+ dict(opt="audit_type", type=Options.T_STR, default=None, required=True, multi=False, hide=False, choice=Options.AUDITS,
48
+ description_ja="監査の種類を指定します。",
49
+ description_en="Specifies the audit type."),
50
+ dict(opt="clmsg_id", type=Options.T_STR, default=None, required=False, multi=False, hide=False, choice=None,
51
+ description_ja="クライアントのメッセージIDを指定します。省略した場合はuuid4で生成されます。",
52
+ description_en="Specifies the message ID of the client. If omitted, uuid4 will be generated."),
53
+ dict(opt="clmsg_date", type=Options.T_DATETIME, default=None, required=False, multi=False, hide=False, choice=None,
54
+ description_ja="クライアントのメッセージ発生日時を指定します。省略した場合はサーバーの現在日時が使用されます。",
55
+ description_en="Specifies the date and time the client message occurred. If omitted, the server's current date/time is used."),
56
+ dict(opt="clmsg_src", type=Options.T_STR, default=None, required=False, multi=False, hide=False, choice=None,
57
+ description_ja="クライアントのメッセージの発生源を指定します。通常 `cmdbox.app.feature.Feature` を継承したクラス名を指定します。",
58
+ description_en="Specifies the source of client messages. Usually specifies the name of a class that extends `cmdbox.app.feature.Feature` ."),
59
+ dict(opt="clmsg_title", type=Options.T_STR, default=None, required=False, multi=False, hide=False, choice=None,
60
+ description_ja="クライアントのメッセージタイトルを指定します。通常コマンドタイトルを指定します。",
61
+ description_en="Specifies the client message title. Usually specifies the command title."),
62
+ dict(opt="clmsg_user", type=Options.T_STR, default=None, required=False, multi=False, hide=False, choice=None,
63
+ description_ja="クライアントのメッセージを発生させたユーザーを指定します。",
64
+ description_en="SpecSpecifies the user who generated the client message."),
65
+ dict(opt="clmsg_body", type=Options.T_DICT, default=None, required=False, multi=True, hide=False, choice=None,
66
+ description_ja="クライアントのメッセージの本文を辞書形式で指定します。",
67
+ description_en="Specifies the body of the client's message in dictionary format."),
68
+ dict(opt="clmsg_tag", type=Options.T_STR, default=None, required=False, multi=True, hide=False, choice=None,
69
+ description_ja="クライアントのメッセージのタグを指定します。後で検索しやすくするために指定します。",
70
+ description_en="Specifies the tag for the client's message. Specify to make it easier to search later."),
71
+ dict(opt="retention_period_days", type=Options.T_INT, default=365, required=False, multi=False, hide=True, choice=None, web="mask",
72
+ description_ja="監査を保存する日数を指定します。この日数より古い監査は削除します。0以下を指定すると無期限で保存されます。",
73
+ description_en="Specify the number of days to keep the audit. If the number is less than or equal to 0, the audit will be kept indefinitely."),
74
+ ]
75
+ return opt
76
+
77
+ def get_svcmd(self):
78
+ """
79
+ この機能のサーバー側のコマンドを返します
80
+
81
+ Returns:
82
+ str: サーバー側のコマンド
83
+ """
84
+ return 'audit_write'
85
+
86
+ def apprun(self, logger:logging.Logger, args:argparse.Namespace, tm:float, pf:List[Dict[str, float]]=[]) -> Tuple[int, Dict[str, Any], Any]:
87
+ """
88
+ この機能の実行を行います
89
+
90
+ Args:
91
+ logger (logging.Logger): ロガー
92
+ args (argparse.Namespace): 引数
93
+ tm (float): 実行開始時間
94
+ pf (List[Dict[str, float]]): 呼出元のパフォーマンス情報
95
+
96
+ Returns:
97
+ Tuple[int, Dict[str, Any], Any]: 終了コード, 結果, オブジェクト
98
+ """
99
+ if args.svname is None:
100
+ msg = dict(warn=f"Please specify the --svname option.")
101
+ common.print_format(msg, False, tm, args.output_json, args.output_json_append, pf=pf)
102
+ return 1, msg, None
103
+ if args.audit_type is None:
104
+ msg = dict(warn=f"Please specify the --audit_type option.")
105
+ common.print_format(msg, False, tm, args.output_json, args.output_json_append, pf=pf)
106
+ return 1, msg, None
107
+ if args.clmsg_id is None:
108
+ args.clmsg_id = str(uuid.uuid4())
109
+ if args.clmsg_date is None:
110
+ args.clmsg_date = datetime.now().strftime('%Y-%m-%d %H:%M:%S') + common.get_tzoffset_str()
111
+ if hasattr(args, 'client_only') and args.client_only==True:
112
+ # クライアントのみの場合は、サーバーへの接続を行わない
113
+ logger.warning(f"client_only is True. Not connecting to server. Skip writing the audit log.")
114
+ ret = dict(success={k:v for k, v in vars(args).items() if v})
115
+ common.print_format(ret, False, tm, args.output_json, args.output_json_append, pf=pf)
116
+ return 0, ret, None
117
+
118
+ audit_type_b64 = convert.str2b64str(args.audit_type)
119
+ clmsg_id_b64 = convert.str2b64str(args.clmsg_id)
120
+ clmsg_date_b64 = convert.str2b64str(args.clmsg_date)
121
+ clmsg_src_b64 = convert.str2b64str(args.clmsg_src) if args.clmsg_src is not None else ''
122
+ clmsg_title_b64 = convert.str2b64str(args.clmsg_title) if args.clmsg_title is not None else ''
123
+ clmsg_user_b64 = convert.str2b64str(args.clmsg_user) if args.clmsg_user is not None else ''
124
+ clmsg_body_str = json.dumps(args.clmsg_body, default=common.default_json_enc, ensure_ascii=False) if args.clmsg_body is not None else '{}'
125
+ clmsg_body_b64 = convert.str2b64str(clmsg_body_str)
126
+ clmsg_tag_str = json.dumps(args.clmsg_tag, default=common.default_json_enc, ensure_ascii=False) if args.clmsg_tag is not None else '[]'
127
+ clmsg_tag_b64 = convert.str2b64str(clmsg_tag_str)
128
+ pg_enabled = args.pg_enabled
129
+ pg_host_b64 = convert.str2b64str(args.pg_host)
130
+ pg_port = args.pg_port if isinstance(args.pg_port, int) else None
131
+ pg_user_b64 = convert.str2b64str(args.pg_user)
132
+ pg_password_b64 = convert.str2b64str(args.pg_password)
133
+ pg_dbname_b64 = convert.str2b64str(args.pg_dbname)
134
+
135
+ if logger.level == logging.DEBUG:
136
+ logger.debug(f"audit write args: {args}")
137
+ cl = client.Client(logger, redis_host=args.host, redis_port=args.port, redis_password=args.password, svname=args.svname)
138
+ cl.redis_cli.send_cmd(self.get_svcmd(),
139
+ [audit_type_b64, clmsg_id_b64, clmsg_date_b64, clmsg_src_b64, clmsg_title_b64, clmsg_user_b64, clmsg_body_b64, clmsg_tag_b64,
140
+ pg_enabled, pg_host_b64, pg_port, pg_user_b64, pg_password_b64, pg_dbname_b64,
141
+ args.retention_period_days],
142
+ retry_count=args.retry_count, retry_interval=args.retry_interval, timeout=args.timeout, nowait=True)
143
+ ret = dict(success=True)
144
+ #common.print_format(ret, False, tm, None, False, pf=pf)
145
+ return 0, ret, cl
146
+
147
+ def is_cluster_redirect(self):
148
+ """
149
+ クラスター宛のメッセージの場合、メッセージを転送するかどうかを返します
150
+
151
+ Returns:
152
+ bool: メッセージを転送する場合はTrue
153
+ """
154
+ return False
155
+
156
+ def svrun(self, data_dir:Path, logger:logging.Logger, redis_cli:redis_client.RedisClient, msg:List[str],
157
+ sessions:Dict[str, Dict[str, Any]]) -> int:
158
+ """
159
+ この機能のサーバー側の実行を行います
160
+
161
+ Args:
162
+ data_dir (Path): データディレクトリ
163
+ logger (logging.Logger): ロガー
164
+ redis_cli (redis_client.RedisClient): Redisクライアント
165
+ msg (List[str]): 受信メッセージ
166
+ sessions (Dict[str, Dict[str, Any]]): セッション情報
167
+
168
+ Returns:
169
+ int: 終了コード
170
+ """
171
+ if logger.level == logging.DEBUG:
172
+ logger.debug(f"audit write svrun msg: {msg}")
173
+ audit_type = convert.b64str2str(msg[2])
174
+ clmsg_id = convert.b64str2str(msg[3])
175
+ clmsg_date = convert.b64str2str(msg[4])
176
+ clmsg_src = convert.b64str2str(msg[5])
177
+ clmsg_title = convert.b64str2str(msg[6])
178
+ clmsg_user = convert.b64str2str(msg[7])
179
+ clmsg_body = convert.b64str2str(msg[8])
180
+ clmsg_tags = convert.b64str2str(msg[9])
181
+ pg_enabled = True if msg[10]=='True' else False
182
+ pg_host = convert.b64str2str(msg[11])
183
+ pg_port = int(msg[12]) if msg[12]!='None' else None
184
+ pg_user = convert.b64str2str(msg[13])
185
+ pg_password = convert.b64str2str(msg[14])
186
+ pg_dbname = convert.b64str2str(msg[15])
187
+ retention_period_days = int(msg[16]) if msg[16] != 'None' else None
188
+ svmsg_id = str(uuid.uuid4())
189
+ st = self.write(msg[1], audit_type, clmsg_id, clmsg_date, clmsg_src, clmsg_title, clmsg_user, clmsg_body, clmsg_tags, svmsg_id,
190
+ pg_enabled, pg_host, pg_port, pg_user, pg_password, pg_dbname,
191
+ retention_period_days,
192
+ data_dir, logger, redis_cli)
193
+ return st
194
+
195
+ def write(self, reskey:str, audit_type:str, clmsg_id:str, clmsg_date:str, clmsg_src:str, clmsg_title:str,
196
+ clmsg_user:str, clmsg_body:str, clmsg_tags:str, svmsg_id:str,
197
+ pg_enabled:bool, pg_host:str, pg_port:int, pg_user:str, pg_password:str, pg_dbname:str,
198
+ retention_period_days:int,
199
+ data_dir:Path, logger:logging.Logger, redis_cli:redis_client.RedisClient) -> int:
200
+ """
201
+ 監査ログを書き込む
202
+
203
+ Args:
204
+ reskey (str): レスポンスキー
205
+ audit_type (str): 監査の種類
206
+ clmsg_id (str): クライアントメッセージID
207
+ clmsg_date (str): クライアントメッセージ発生日時
208
+ clmsg_src (str): クライアントメッセージの発生源
209
+ clmsg_title (str): クライアントメッセージのタイトル
210
+ clmsg_user (str): クライアントメッセージの発生させたユーザー
211
+ clmsg_body (str): クライアントメッセージの本文
212
+ clmsg_tags (str): クライアントメッセージのタグ
213
+ svmsg_id (str): サーバーメッセージID
214
+ pg_enabled (bool): PostgreSQLを使用する場合はTrue
215
+ pg_host (str): PostgreSQLホスト
216
+ pg_port (int): PostgreSQLポート
217
+ pg_user (str): PostgreSQLユーザー
218
+ pg_password (str): PostgreSQLパスワード
219
+ pg_dbname (str): PostgreSQLデータベース名
220
+ retention_period_days (int): 監査を保存する日数
221
+ data_dir (Path): データディレクトリ
222
+ logger (logging.Logger): ロガー
223
+ redis_cli (redis_client.RedisClient): Redisクライアント
224
+
225
+ Returns:
226
+ int: レスポンスコード
227
+ """
228
+ try:
229
+ with self.initdb(data_dir, logger, pg_enabled, pg_host, pg_port, pg_user, pg_password, pg_dbname) as conn:
230
+ cursor = conn.cursor()
231
+ try:
232
+ svmsg_date = datetime.now().strftime('%Y-%m-%d %H:%M:%S') + common.get_tzoffset_str()
233
+ if not pg_enabled:
234
+ cursor.execute('''
235
+ INSERT INTO audit (audit_type, clmsg_id, clmsg_date, clmsg_src, clmsg_title, clmsg_user, clmsg_body, clmsg_tag,
236
+ svmsg_id, svmsg_date)
237
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
238
+ ''', (audit_type, clmsg_id, clmsg_date, clmsg_src, clmsg_title, clmsg_user, clmsg_body, clmsg_tags, svmsg_id, svmsg_date))
239
+ if retention_period_days is not None and retention_period_days > 0:
240
+ cursor.execute('DELETE FROM audit WHERE svmsg_date < datetime(CURRENT_TIMESTAMP, ?)',
241
+ (f'-{retention_period_days} days',))
242
+ else:
243
+ cursor.execute('''
244
+ INSERT INTO audit (audit_type, clmsg_id, clmsg_date, clmsg_src, clmsg_title, clmsg_user, clmsg_body, clmsg_tag,
245
+ svmsg_id, svmsg_date)
246
+ VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
247
+ ''', (audit_type, clmsg_id, clmsg_date, clmsg_src, clmsg_title, clmsg_user, clmsg_body, clmsg_tags, svmsg_id, svmsg_date))
248
+ if retention_period_days is not None and retention_period_days > 0:
249
+ cursor.execute("DELETE FROM audit WHERE svmsg_date < CURRENT_TIMESTAMP + %s ",
250
+ (f'-{retention_period_days} day',))
251
+ conn.commit()
252
+ rescode, msg = (self.RESP_SCCESS, dict(success=True))
253
+ redis_cli.rpush(reskey, msg)
254
+ return rescode
255
+ finally:
256
+ cursor.close()
257
+ except Exception as e:
258
+ logger.warning(f"Failed to write: {e}", exc_info=True)
259
+ redis_cli.rpush(reskey, dict(warn=f"Failed to write: {e}"))
260
+ return self.RESP_WARN
@@ -39,13 +39,13 @@ class CmdList(feature.OneshotResultEdgeFeature):
39
39
  description_ja="データフォルダ配下のコマンドリストを取得します。",
40
40
  description_en="Obtains a list of commands under the data folder.",
41
41
  choice=[
42
- dict(opt="data", type=Options.T_FILE, default=self.default_data, required=True, multi=False, hide=False, choice=None,
42
+ dict(opt="data", type=Options.T_DIR, default=self.default_data, required=True, multi=False, hide=False, choice=None,
43
43
  description_ja=f"省略した時は `$HONE/.{self.ver.__appid__}` を使用します。",
44
44
  description_en=f"When omitted, `$HONE/.{self.ver.__appid__}` is used."),
45
45
  dict(opt="kwd", type=Options.T_STR, default=None, required=False, multi=False, hide=False, choice=None,
46
46
  description_ja=f"検索したいコマンド名を指定します。中間マッチで検索します。",
47
47
  description_en=f"Specify the name of the command you want to search. Search with intermediate matches."),
48
- dict(opt="signin_file", type=Options.T_FILE, default=f".{self.ver.__appid__}/user_list.yml", required=False, multi=False, hide=True, choice=None,
48
+ dict(opt="signin_file", type=Options.T_FILE, default=f".{self.ver.__appid__}/user_list.yml", required=False, multi=False, hide=True, choice=None, fileio="in",
49
49
  description_ja="サインイン可能なユーザーとパスワードを記載したファイルを指定します。",
50
50
  description_en="Specify a file containing users and passwords with which they can signin."),
51
51
  dict(opt="groups", type=Options.T_STR, default=None, required=False, multi=True, hide=True, choice=None,
@@ -39,13 +39,13 @@ class CmdLoad(feature.OneshotResultEdgeFeature):
39
39
  description_ja="データフォルダ配下のコマンドの内容を取得します。",
40
40
  description_en="Obtains the contents of commands under the data folder.",
41
41
  choice=[
42
- dict(opt="data", type=Options.T_FILE, default=self.default_data, required=True, multi=False, hide=False, choice=None,
42
+ dict(opt="data", type=Options.T_DIR, default=self.default_data, required=True, multi=False, hide=False, choice=None,
43
43
  description_ja=f"省略した時は `$HONE/.{self.ver.__appid__}` を使用します。",
44
44
  description_en=f"When omitted, `$HONE/.{self.ver.__appid__}` is used."),
45
45
  dict(opt="title", type=Options.T_STR, default=None, required=False, multi=False, hide=False, choice=None,
46
46
  description_ja=f"読込みたいコマンド名を指定します。",
47
47
  description_en=f"Specify the name of the command to be read."),
48
- dict(opt="signin_file", type=Options.T_FILE, default=f".{self.ver.__appid__}/user_list.yml", required=False, multi=False, hide=True, choice=None,
48
+ dict(opt="signin_file", type=Options.T_FILE, default=f".{self.ver.__appid__}/user_list.yml", required=False, multi=False, hide=True, choice=None, fileio="in",
49
49
  description_ja="サインイン可能なユーザーとパスワードを記載したファイルを指定します。",
50
50
  description_en="Specify a file containing users and passwords with which they can signin."),
51
51
  dict(opt="groups", type=Options.T_STR, default=None, required=False, multi=True, hide=True, choice=None,
@@ -40,7 +40,7 @@ class EdgeConfig(feature.UnsupportEdgeFeature):
40
40
  dict(opt="endpoint", type=Options.T_STR, default="http://localhost:8081", required=False, multi=False, hide=False, choice=None,
41
41
  description_ja="エンドポイントのURLを指定します。",
42
42
  description_en="Specify the URL of the endpoint."),
43
- dict(opt="icon_path", type=Options.T_FILE, default=Path(self.ver.__file__).parent / 'web' / 'assets' / self.ver.__appid__ / 'favicon.ico',
43
+ dict(opt="icon_path", type=Options.T_FILE, default=Path(self.ver.__file__).parent / 'web' / 'assets' / self.ver.__appid__ / 'favicon.ico', fileio="in",
44
44
  required=False, multi=False, hide=False, choice=None,
45
45
  description_ja="アイコン画像のパスを指定します。",
46
46
  description_en="Specify the path to the icon image."),
@@ -94,7 +94,7 @@ class EdgeConfig(feature.UnsupportEdgeFeature):
94
94
  dict(opt="saml_timeout", type=Options.T_INT, default="60", required=False, multi=False, hide=False, choice=None,
95
95
  description_ja="SAML認証が完了するまでのタイムアウト時間を指定します。",
96
96
  description_en="Specify the timeout period before SAML authentication completes."),
97
- dict(opt="data", type=Options.T_FILE, default=self.default_data, required=False, multi=False, hide=True, choice=None,
97
+ dict(opt="data", type=Options.T_DIR, default=self.default_data, required=False, multi=False, hide=True, choice=None,
98
98
  description_ja=f"省略した時は f`$HONE/.{self.ver.__appid__}` を使用します。",
99
99
  description_en=f"When omitted, f`$HONE/.{self.ver.__appid__}` is used."),
100
100
  dict(opt="svcert_no_verify", type=Options.T_BOOL, default=False, required=False, multi=False, hide=True, choice=[False, True],
@@ -36,7 +36,7 @@ class EdgeStart(feature.UnsupportEdgeFeature):
36
36
  description_ja="端末モードを起動します。",
37
37
  description_en="Start Edge mode.",
38
38
  choice=[
39
- dict(opt="data", type=Options.T_FILE, default=self.default_data, required=True, multi=False, hide=True, choice=None,
39
+ dict(opt="data", type=Options.T_DIR, default=self.default_data, required=True, multi=False, hide=True, choice=None,
40
40
  description_ja=f"省略した時は f`$HONE/.{self.ver.__appid__}` を使用します。",
41
41
  description_en=f"When omitted, f`$HONE/.{self.ver.__appid__}` is used."),
42
42
  ]