Ver Fonte

feat: 添加用户管理系统

SongZihuan há 3 anos atrás
pai
commit
b5b5df5c46
7 ficheiros alterados com 358 adições e 30 exclusões
  1. 21 1
      core/user.py
  2. 52 1
      sql/user.py
  3. 10 0
      static/styles/auth/role.css
  4. 27 27
      templates/auth/delete.html
  5. 151 0
      templates/auth/role.html
  6. 3 1
      templates/auth/yours.html
  7. 94 0
      view/auth.py

+ 21 - 1
core/user.py

@@ -4,7 +4,16 @@ from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
 from typing import Optional
 
 from configure import conf
-from sql.user import read_user, check_role, get_user_email, create_user, get_role_name, delete_user, change_passwd_hash
+from sql.user import (read_user,
+                      check_role,
+                      get_user_email,
+                      create_user,
+                      get_role_name,
+                      delete_user,
+                      change_passwd_hash,
+                      create_role,
+                      delete_role,
+                      set_user_role)
 import core.blog
 import core.comment
 import core.msg
@@ -128,3 +137,14 @@ class User(UserMixin):
 
     def change_passwd(self, passwd):
         return change_passwd_hash(self.user_id, self.get_passwd_hash(passwd))
+
+    @staticmethod
+    def create_role(name: str, authority):
+        return create_role(name, authority)
+
+    @staticmethod
+    def delete_role(name: str):
+        return delete_role(name)
+
+    def set_user_role(self, name: str):
+        return set_user_role(name, self.user_id)

+ 52 - 1
sql/user.py

@@ -2,6 +2,14 @@ from sql import db
 from sql.base import DBBit
 import core.user
 
+from typing import List
+
+
+role_authority = ["WriteBlog", "WriteComment", "WriteMsg", "CreateUser",
+                  "ReadBlog", "ReadComment", "ReadMsg", "ReadSecretMsg", "ReadUserInfo",
+                  "DeleteBlog", "DeleteComment", "DeleteMsg", "DeleteUser",
+                  "ConfigureSystem", "ReadSystem"]
+
 
 def read_user(email: str):
     """ 读取用户 """
@@ -39,6 +47,41 @@ def delete_user(user_id: int):
     return True
 
 
+def create_role(name: str, authority: List[str]):
+    cur = db.insert(table="role", columns=["RoleName"], values=f"'{name}'", not_commit=True)
+    if cur is None or cur.rowcount == 0:
+        return False
+
+    kw = {}
+    for i in role_authority:
+        kw[i] = '0'
+    for i in authority:
+        if i in role_authority:
+            kw[i] = '1'
+
+    cur = db.update(table='role', kw=kw, where=f"RoleName='{name}'")
+    if cur is None or cur.rowcount == 0:
+        return False
+    return True
+
+
+def delete_role(name: str):
+    cur = db.delete(table="role", where=f"RoleName='{name}'")
+    if cur is None or cur.rowcount == 0:
+        return False
+    return True
+
+
+def set_user_role(name: str, user_id: str):
+    role_id = get_role_id_by_name(name)
+    if role_id is None:
+        return False
+    cur = db.update(table="user", kw={"Role": f"{role_id}"}, where=f"ID={user_id}")
+    if cur is None or cur.rowcount == 0:
+        return False
+    return True
+
+
 def change_passwd_hash(user_id: int, passwd_hash: str):
     cur = db.update(table='user', kw={'PasswdHash': f"'{passwd_hash}'"}, where=f'ID={user_id}')
     if cur is None or cur.rowcount == 0:
@@ -72,7 +115,15 @@ def check_role(role: int, operate: str):
 
 def check_role_by_name(role: str, operate: str):
     """ 检查角色权限(通过角色名) """
-    cur = db.search(columns=[operate], table="role", where=f"RoleName='{role}")
+    cur = db.search(columns=[operate], table="role", where=f"RoleName='{role}'")
     if cur is None or cur.rowcount == 0:
         return False
     return cur.fetchone()[0] == DBBit.BIT_1
+
+
+def get_role_id_by_name(role: str):
+    """ 检查角色权限(通过角色名) """
+    cur = db.search(columns=["RoleID"], table="role", where=f"RoleName='{role}'")
+    if cur is None or cur.rowcount == 0:
+        return None
+    return cur.fetchone()[0]

+ 10 - 0
static/styles/auth/role.css

@@ -0,0 +1,10 @@
+.role-form {
+    background-color: white;
+    border-radius: 10px;
+    border: 2px solid #6b6882;
+    padding: 15px;
+}
+
+#RoleTabDiv {
+    min-height: 30vh;
+}

+ 27 - 27
templates/auth/delete.html

@@ -1,6 +1,6 @@
 {% extends "base.html" %}
 
-{% block title %} 修改密码 {% endblock %}
+{% block title %} 删除用户 {% endblock %}
 
 {% block style %}
     {{ super() }}
@@ -9,38 +9,38 @@
 
 {% block context %}
     <section id="base" class="container mt-3">
-    <div class="row">
-        <div class="col-12 col-lg-6 offset-lg-3">
-            <form method="post" action="{{ url_for("auth.delete_user_page") }}" class="delete-form">
-                {{ DeleteUserForm.hidden_tag() }}
+        <div class="row">
+            <div class="col-12 col-lg-6 offset-lg-3">
+                <form method="post" action="{{ url_for("auth.delete_user_page") }}" class="delete-form">
+                    {{ DeleteUserForm.hidden_tag() }}
 
-                <div class="form-group">
-                    {{ DeleteUserForm.email.label }}
-                    {{ DeleteUserForm.email(class="form-control") }}
-                </div>
+                    <div class="form-group">
+                        {{ DeleteUserForm.email.label }}
+                        {{ DeleteUserForm.email(class="form-control") }}
+                    </div>
 
-                <div id="DeleteModal" class="modal fade" role="dialog" aria-hidden="true">
-                    <div class="modal-dialog">
-                        <div class="modal-content">
-                            <div class="modal-header">
-                                <h4 class="modal-title"> 删除用户? </h4>
-                            </div>
-                            <div class="modal-body">
-                                <p> 是否确认删除用户? </p>
-                            </div>
-                            <div class="modal-footer">
-                                {{ DeleteUserForm.submit(class='btn btn-danger', value='确认') }}
-                                <button type="button" class="btn btn-secondary" data-dismiss="modal">取消</button>
+                    <div id="DeleteModal" class="modal fade" role="dialog" aria-hidden="true">
+                        <div class="modal-dialog">
+                            <div class="modal-content">
+                                <div class="modal-header">
+                                    <h4 class="modal-title"> 删除用户? </h4>
+                                </div>
+                                <div class="modal-body">
+                                    <p> 是否确认删除用户? </p>
+                                </div>
+                                <div class="modal-footer">
+                                    {{ DeleteUserForm.submit(class='btn btn-danger', value='确认') }}
+                                    <button type="button" class="btn btn-secondary" data-dismiss="modal">取消</button>
+                                </div>
                             </div>
                         </div>
                     </div>
-                </div>
 
-                <div class="text-right">
-                    <a class="btn btn-danger" data-toggle="modal" data-target="#DeleteModal"> 删除用户 </a>
-                </div>
-            </form>
+                    <div class="text-right">
+                        <a class="btn btn-danger" data-toggle="modal" data-target="#DeleteModal"> 删除用户 </a>
+                    </div>
+                </form>
+            </div>
         </div>
-    </div>
     </section>
 {% endblock %}

+ 151 - 0
templates/auth/role.html

@@ -0,0 +1,151 @@
+{% extends "base.html" %}
+
+{% block title %} 角色管理 {% endblock %}
+
+{% block style %}
+    {{ super() }}
+    <link href="{{ url_for('static', filename='styles/auth/role.css') }}" rel="stylesheet">
+{% endblock %}
+
+{% block context %}
+    <section id="base" class="container mt-3">
+        <div class="row">
+            <div class="col-12 col-lg-6 offset-lg-3">
+                <ul id="RoleTab" class="nav nav-pills justify-content-center mb-2">
+                    <li class="active nav-item">
+                        <a href="#create" class="nav-link" data-toggle="tab"> 创建角色 </a>
+                    </li>
+                    <li class="nav-item">
+                        <a href="#drop" class="nav-link" data-toggle="tab"> 删除角色 </a>
+                    </li>
+                    <li class="nav-item">
+                        <a href="#set" class="nav-link" data-toggle="tab"> 设置角色 </a>
+                    </li>
+                </ul>
+
+                <div id="RoleTabDiv">
+                    <div id="RoleTabContent" class="tab-content">
+                        <div class="tab-pane fade active" id="create">
+                            <form method="post" action="{{ url_for('auth.role_create_page') }}" class="role-form">
+                                {{ CreateRoleForm.hidden_tag() }}
+
+                                <div class="form-group">
+                                    {{ CreateRoleForm.name.label }}
+                                    {{ CreateRoleForm.name(class="form-control") }}
+                                </div>
+
+                                <div class="form-group">
+                                    {{ CreateRoleForm.authority.label }}
+                                    {{ CreateRoleForm.authority(class="form-control") }}
+                                </div>
+
+                                <div id="CreateModal" class="modal fade" role="dialog" aria-hidden="true">
+                                    <div class="modal-dialog">
+                                        <div class="modal-content">
+                                            <div class="modal-header">
+                                                <h4 class="modal-title"> 创建角色? </h4>
+                                            </div>
+                                            <div class="modal-body">
+                                                <p> 是否确认创建角色? </p>
+                                            </div>
+                                            <div class="modal-footer">
+                                                {{ CreateRoleForm.submit(class='btn btn-info', value='确认') }}
+                                                <button type="button" class="btn btn-secondary" data-dismiss="modal">
+                                                    取消
+                                                </button>
+                                            </div>
+                                        </div>
+                                    </div>
+                                </div>
+
+                                <div class="text-right">
+                                    <a class="btn btn-info" data-toggle="modal" data-target="#CreateModal"> 创建角色 </a>
+                                </div>
+                            </form>
+                        </div>
+
+                        <div class="tab-pane fade" id="drop">
+                            <form method="post" action="{{ url_for('auth.role_delete_page') }}" class="role-form">
+                                {{ DeleteRoleForm.hidden_tag() }}
+
+                                <div class="form-group">
+                                    {{ DeleteRoleForm.name.label }}
+                                    {{ DeleteRoleForm.name(class="form-control") }}
+                                </div>
+
+                                <div id="DeleteModal" class="modal fade" role="dialog" aria-hidden="true">
+                                    <div class="modal-dialog">
+                                        <div class="modal-content">
+                                            <div class="modal-header">
+                                                <h4 class="modal-title"> 删除角色? </h4>
+                                            </div>
+                                            <div class="modal-body">
+                                                <p> 是否确认删除角色? </p>
+                                            </div>
+                                            <div class="modal-footer">
+                                                {{ DeleteRoleForm.submit(class='btn btn-danger', value='确认') }}
+                                                <button type="button" class="btn btn-secondary" data-dismiss="modal">
+                                                    取消
+                                                </button>
+                                            </div>
+                                        </div>
+                                    </div>
+                                </div>
+
+                                <div class="text-right">
+                                    <a class="btn btn-danger" data-toggle="modal" data-target="#DeleteModal"> 删除角色 </a>
+                                </div>
+                            </form>
+                        </div>
+
+                        <div class="tab-pane fade" id="set">
+                            <form method="post" action="{{ url_for('auth.role_set_page') }}" class="role-form">
+                                {{ SetRoleForm.hidden_tag() }}
+
+                                <div class="form-group">
+                                    {{ SetRoleForm.email.label }}
+                                    {{ SetRoleForm.email(class="form-control") }}
+                                </div>
+
+                                <div class="form-group">
+                                    {{ SetRoleForm.name.label }}
+                                    {{ SetRoleForm.name(class="form-control") }}
+                                </div>
+
+                                <div id="SetModal" class="modal fade" role="dialog" aria-hidden="true">
+                                    <div class="modal-dialog">
+                                        <div class="modal-content">
+                                            <div class="modal-header">
+                                                <h4 class="modal-title"> 设置角色? </h4>
+                                            </div>
+                                            <div class="modal-body">
+                                                <p> 是否确认设置角色? </p>
+                                            </div>
+                                            <div class="modal-footer">
+                                                {{ SetRoleForm.submit(class='btn btn-danger', value='确认') }}
+                                                <button type="button" class="btn btn-secondary" data-dismiss="modal">
+                                                    取消
+                                                </button>
+                                            </div>
+                                        </div>
+                                    </div>
+                                </div>
+
+                                <div class="text-right">
+                                    <a class="btn btn-danger" data-toggle="modal" data-target="#SetModal"> 设置角色 </a>
+                                </div>
+                            </form>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </section>
+{% endblock %}
+
+{% block javascript %}
+    {{ super() }}
+    <script>
+        $('#RoleTab .active a').tab('show');  // 显示第一个 tab
+    </script>
+{% endblock %}

+ 3 - 1
templates/auth/yours.html

@@ -42,7 +42,9 @@
                     {% if current_user.check_role('DeleteUser') %}
                         <a class="card-link" href="{{ url_for('auth.delete_user_page') }}"> 删除用户 </a>
                     {% endif %}
-
+                    {% if current_user.check_role('ConfigureSystem') %}
+                        <a class="card-link" href="{{ url_for('auth.role_page') }}"> 角色设置 </a>
+                    {% endif %}
                 </div>
             </div>
         </div>

+ 94 - 0
view/auth.py

@@ -53,6 +53,23 @@ class DeleteUserForm(FlaskForm):
             raise ValidationError("邮箱用户不存在")
 
 
+class CreateRoleForm(FlaskForm):
+    name = StringField("角色名称", validators=[DataRequired(), Length(1, 20)])
+    authority = StringField("权限", validators=[Length(0, 100)])
+    submit = SubmitField("创建角色")
+
+
+class DeleteRoleForm(FlaskForm):
+    name = StringField("角色名称", validators=[DataRequired(), Length(1, 20)])
+    submit = SubmitField("删除角色")
+
+
+class SetRoleForm(FlaskForm):
+    email = StringField("邮箱", validators=[DataRequired(), Length(1, 32)])
+    name = StringField("角色名称", validators=[DataRequired(), Length(1, 20)])
+    submit = SubmitField("设置角色")
+
+
 @auth.route('/yours')
 @login_required
 def yours_page():
@@ -142,6 +159,10 @@ def change_passwd_page():
 @auth.route('/delete', methods=['GET', 'POST'])
 @login_required
 def delete_user_page():
+    if not current_user.check_role("DeleteUser"):
+        abort(403)
+        return
+
     form = DeleteUserForm()
     if form.validate_on_submit():
         user = load_user_by_email(form.email.data)
@@ -157,6 +178,79 @@ def delete_user_page():
     return render_template("auth/delete.html", DeleteUserForm=form)
 
 
+@auth.route('/role', methods=['GET'])
+@login_required
+def role_page():
+    if not current_user.check_role("ConfigureSystem"):
+        abort(403)
+        return
+    return render_template("auth/role.html",
+                           CreateRoleForm=CreateRoleForm(),
+                           DeleteRoleForm=DeleteRoleForm(),
+                           SetRoleForm=SetRoleForm())
+
+
+@auth.route('/role-create', methods=['POST'])
+@login_required
+def role_create_page():
+    form = CreateRoleForm()
+    if form.validate_on_submit():
+        if not current_user.check_role("ConfigureSystem"):
+            abort(403)
+            return
+
+        if User.create_role(form.name.data, form.authority.data.replace(" ", "").split(";")):
+            flash("角色创建成功")
+        else:
+            flash("角色创建失败")
+        return redirect(url_for("auth.role_page"))
+
+    abort(404)
+    return
+
+
+@auth.route('/role-delete', methods=['POST'])
+@login_required
+def role_delete_page():
+    form = DeleteRoleForm()
+    if form.validate_on_submit():
+        if not current_user.check_role("ConfigureSystem"):
+            abort(403)
+            return
+
+        if User.delete_role(form.name.data):
+            flash("角色删除成功")
+        else:
+            flash("角色删除失败")
+        return redirect(url_for("auth.role_page"))
+
+    abort(404)
+    return
+
+
+@auth.route('/role-set', methods=['POST'])
+@login_required
+def role_set_page():
+    form = SetRoleForm()
+    if form.validate_on_submit():
+        if not current_user.check_role("ConfigureSystem"):
+            abort(403)
+            return
+
+        user = load_user_by_email(form.email.data)
+        if user is not None:
+            if user.set_user_role(form.name.data):
+                flash("角色设置成功")
+            else:
+                flash("角色设置失败")
+        else:
+            flash("邮箱未注册")
+        return redirect(url_for("auth.role_page"))
+
+    abort(404)
+    return
+
+
 @auth.context_processor
 def inject_base():
     return {"top_nav": ["", "", "", "", "", "active"]}