暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

openGauss数据库源码解析系列文章——安全管理源码解析:角色管理

Gauss松鼠会 2022-03-18
970

Gauss松鼠会

学习 探索 分享数据库前沿知识和技术 共建数据库技术交流圈

关注

上篇图文,我们分享了安全认证中认证机制、Kerberos安全认证精彩内容本篇将详细介绍安全管理源码解析——角色管理的相关内容

9.3  角色管理

角色是拥有数据库对象和权限的实体,在不同的环境中角色可以认为是一个用户、一个组或者兼顾两者。角色管理包含了角色的创建、修改、删除、权限授予和回收操作。

9.3.1  角色创建

如果在openGauss上需要创建一个角色,可以使用SQL命令CREATE ROLE,其语法为:

    CREATE ROLE role_name [ [ WITH ] option [ ... ] ] [ ENCRYPTED | UNENCRYPTED ] { PASSWORD | IDENTIFIED BY } { 'password' | DISABLE };

    创建角色是通过函数CreateRole实现的,其函数接口为

      void CreateRole(CreateRoleStmt* stmt)

      其中CreateRoleStmt为创建角色时所需的数据结构,具体数据结构代码如下:

        typedef struct CreateRoleStmt {
        NodeTag type;
        RoleStmtType stmt_type; /* 将要创建的角色类型 ROLE/USER/GROUP */
        char* role; /* 角色名 */
        List* options; /* 角色属性列表 */
        } CreateRoleStmt;

        字段stmt_type是枚举类型,相关代码如下:

          typedef enum RoleStmtType {
          ROLESTMT_ROLE, /* 代表创建角色 */
          ROLESTMT_USER, /* 代表创建用户 */
          ROLESTMT_GROUP, /* 代表创建组用户 */
          } RoleStmtType;

          字段option用来存储角色的属性信息,具体的数据结构为:

            typedef struct DefElem {
            NodeTag type;
            char* defnamespace; /* 节点对应的命名空间 */
            char* defname; /* 节点对应的角色属性名 */
            Node* arg; /* 表示值或类型名 */
            DefElemAction defaction; /* SET/ADD/DROP 等其他未指定的行为 */
            } DefElem;
            在上述的关键数据结构基础之上,完整的创建角色流程如图1所示

            图1  openGauss角色创建流程

            创建角色时先判断所要创建的角色类型如果是创建用户,则设置其canlogin属性为true因为用户默认具有登录权限。而创建角色和创建组时,若角色属性参数没有声明的话,则canlogin属性默认为false。相关代码如下:

              /* 默认值可能因原始语句类型而异 */
              switch (stmt->stmt_type) {
              case ROLESTMT_ROLE:
              break;
              case ROLESTMT_USER:
              canlogin = true;
              break;
              case ROLESTMT_GROUP:
              break;
              default:
              break;
              }

              检查完所要创建的角色类型以后,开始循环获取角色属性options中的内容,并将其转换成对应的角色属性值类型。相关代码如下:

                /* 从node tree中获取option */
                foreach (option, stmt->options) {
                DefElem* defel = (DefElem*)lfirst(option);

                if (strcmp(defel->defname, "password") == 0 || strcmp(defel->defname, "encryptedPassword") == 0 ||
                strcmp(defel->defname, "unencryptedPassword") == 0) {
                if (dpassword != NULL) {
                clean_role_password(dpassword);
                ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("conflicting or redundant options")));
                }
                dpassword = defel;
                if (strcmp(defel->defname, "encryptedPassword") == 0)
                encrypt_password = true;
                else if (strcmp(defel->defname, "unencryptedPassword") == 0) {
                clean_role_password(dpassword);
                ereport(ERROR,
                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                errmsg("Permission denied to create role with option UNENCRYPTED.")));
                }
                } else if (strcmp(defel->defname, "sysid") == 0) {
                ereport(NOTICE, (errmsg("SYSID can no longer be specified")));
                } else if (strcmp(defel->defname, "inherit") == 0) {
                if (dinherit != NULL) {
                clean_role_password(dpassword);
                ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("conflicting or redundant options")));
                }
                dinherit = defel;
                } else if (strcmp(defel->defname, "createrole") == 0) {
                if (dcreaterole != NULL) {
                clean_role_password(dpassword);
                ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("conflicting or redundant options")));
                }
                dcreaterole = defel;
                } else if (strcmp(defel->defname, "createdb") == 0) {
                if (dcreatedb != NULL) {
                clean_role_password(dpassword);
                ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("conflicting or redundant options")));
                }
                dcreatedb = defel;
                } else if (strcmp(defel->defname, "useft") == 0) {
                if (duseft != NULL) {
                clean_role_password(dpassword);
                ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("conflicting or redundant options")));
                }
                duseft = defel;
                ……

                根据对应的参数信息转换需要的角色属性值类型如提取issuper值和createrole值等。相关代码如下:

                     if (dissuper != NULL)
                  issuper = intVal(dissuper->arg) != 0;
                  if (dinherit != NULL)
                  inherit = intVal(dinherit->arg) != 0;
                  if (dcreaterole != NULL)
                  createrole = intVal(dcreaterole->arg) != 0;
                  if (dcreatedb != NULL)
                  createdb = intVal(dcreatedb->arg) != 0;
                  ……

                  在完成了转换以后,将角色属性值以及角色的信息一起构建一个pg_authid的元组,再写回系统表并更新索引。作相关代码如下:

                    /* 检查pg_authid relation,确认该角色没有存在*/
                    Relation pg_authid_rel = heap_open(AuthIdRelationId, RowExclusiveLock);
                    TupleDesc pg_authid_dsc = RelationGetDescr(pg_authid_rel);

                    if (OidIsValid(get_role_oid(stmt->role, true))) {
                    str_reset(password);
                    ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("role \"%s\" already exists", stmt->role)));
                    }
                    ……
                    /* 创建一个插入的tuple */
                    errno_t errorno = memset_s(new_record, sizeof(new_record), 0, sizeof(new_record));
                    securec_check(errorno, "\0", "\0");
                    errorno = memset_s(new_record_nulls, sizeof(new_record_nulls), false, sizeof(new_record_nulls));
                    securec_check(errorno, "\0", "\0");

                    new_record[Anum_pg_authid_rolname - 1] = DirectFunctionCall1(namein, CStringGetDatum(stmt->role));

                    new_record[Anum_pg_authid_rolsuper - 1] = BoolGetDatum(issuper);
                    new_record[Anum_pg_authid_rolinherit - 1] = BoolGetDatum(inherit);
                    new_record[Anum_pg_authid_rolcreaterole - 1] = BoolGetDatum(createrole);
                    new_record[Anum_pg_authid_rolcreatedb - 1] = BoolGetDatum(createdb);

                    new_record[Anum_pg_authid_rolcatupdate - 1] = BoolGetDatum(issuper);
                    new_record[Anum_pg_authid_rolcanlogin - 1] = BoolGetDatum(canlogin);
                    new_record[Anum_pg_authid_rolreplication - 1] = BoolGetDatum(isreplication);
                    new_record[Anum_pg_authid_rolauditadmin - 1] = BoolGetDatum(isauditadmin);
                    new_record[Anum_pg_authid_rolsystemadmin - 1] = BoolGetDatum(issystemadmin);
                    new_record[Anum_pg_authid_rolconnlimit - 1] = Int32GetDatum(connlimit);
                    ……
                    HeapTuple tuple = heap_form_tuple(pg_authid_dsc, new_record, new_record_nulls);

                    if (u_sess->proc_cxt.IsBinaryUpgrade && OidIsValid(u_sess->upg_cxt.binary_upgrade_next_pg_authid_oid)) {
                    HeapTupleSetOid(tuple, u_sess->upg_cxt.binary_upgrade_next_pg_authid_oid);
                    u_sess->upg_cxt.binary_upgrade_next_pg_authid_oid = InvalidOid;
                    }

                    roleid = simple_heap_insert(pg_authid_rel, tuple);

                    if (IsUnderPostmaster) {
                    if (OidIsValid(rpoid) && (rpoid != DEFAULT_POOL_OID))
                    recordDependencyOnRespool(AuthIdRelationId, roleid, rpoid);

                    u_sess->wlm_cxt->wlmcatalog_update_user = true;
                    }
                    ……

                    完成更新以后,将新创建的角色加入指定存在的父角色中。相关代码如下:

                       /* 将新角色添加到指定的现有角色中 */
                      foreach (item, addroleto) {
                      char* oldrolename = strVal(lfirst(item));
                      Oid oldroleid = get_role_oid(oldrolename, false);

                      AddRoleMems(
                      oldrolename, oldroleid, list_make1(makeString(stmt->role)), list_make1_oid(roleid), GetUserId(), false);
                      }

                      AddRoleMems(stmt->role, roleid, adminmembers, roleNamesToIds(adminmembers), GetUserId(), true);
                      AddRoleMems(stmt->role, roleid, rolemembers, roleNamesToIds(rolemembers), GetUserId(), false);

                      至此就完成了整个角色创建的过程。

                      9.3.2  角色管理

                      1. 修改角色属性

                      修改一个数据库角色可以使用SQL命令ALTER ROLE。角色属性的修改是通过调用AlterRole函数来实现的,该函数只有一个类型为AlterRoleStmt结构的参数。相关代码如下:

                        typedef struct AlterRoleStmt {
                        NodeTag type;
                        char* role; /* 角色的名称 */
                        List* options; /* 需要修改的属性列表 */
                        int action; /* +1增加成员关系, -1删除成员关系 */
                        RoleLockType lockstatus; /* 角色锁定状态 */
                        } AlterRoleStmt;
                        修改角色的流程如2所示

                        图2  openGauss角色管理流程图

                        调用函数AlterRole修改用户角色属性时,首先循环判断options,依次提取要修改的角色属性;然后查看系统表pg_authid判断是否已存在该角色,如果不存在则提示报错;再进行相应的权限判断,检查执行者是否有权限去更改该角色的属性;最后构建一个新的元组,将要更改的属性更新到新元组中,存入系统表pg_authid。同时AlterRole函数也可以用来调整角色的成员关系,结构体中的action字段值设置为1-1分别表示增加和删除成员关系,该选项将在授予和回收角色章节具体描述。AlterRole函数的具体实现代码如下:

                          void AlterRole(AlterRoleStmt* stmt)
                          {
                          . . .
                          /* 循环提取角色的属性options */
                          foreach (option, stmt->options) {
                          DefElem* defel = (DefElem*)lfirst(option);

                          if (strcmp(defel->defname, "password") == 0 || strcmp(defel->defname, "encryptedPassword") == 0 ||
                          strcmp(defel->defname, "unencryptedPassword") == 0) {
                          if (dpassword != NULL) {
                          clean_role_password(dpassword);
                          ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("conflicting or redundant options")));
                          }
                          dpassword = defel;
                          if (strcmp(defel->defname, "encryptedPassword") == 0)
                          encrypt_password = true;
                          else if (strcmp(defel->defname, "unencryptedPassword") == 0) {
                          clean_role_password(dpassword);
                          ereport(ERROR,
                          (errcode(ERRCODE_INVALID_ROLE_SPECIFICATION),
                          errmsg("Permission denied to create role with option UNENCRYPTED.")));
                          }
                          } else if (strcmp(defel->defname, "createrole") == 0) {
                          if (dcreaterole != NULL) {
                          clean_role_password(dpassword);
                          ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("conflicting or redundant options")));
                          }
                          dcreaterole = defel;
                          } else if (strcmp(defel->defname, "inherit") == 0) {
                          if (dinherit != NULL) {
                          clean_role_password(dpassword);
                          ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("conflicting or redundant options")));
                          }
                          dinherit = defel;
                          }
                          . . .
                          else {
                          clean_role_password(dpassword);
                          ereport(ERROR,
                          (errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE), errmsg("option \"%s\" not recognized", defel->defname)));
                          }
                          }
                          /* 将提取的属性赋值给对应的变量 */
                          if (dpassword != NULL && dpassword->arg != NULL) {
                          head = list_head((List*)dpassword->arg);
                          if (head != NULL) {
                          pwdargs = (A_Const*)linitial((List*)dpassword->arg);
                          if (pwdargs != NULL) {
                          password = strVal(&pwdargs->val);
                          }
                          if (lnext(head)) {
                          pwdargs = (A_Const*)lsecond((List*)dpassword->arg);
                          if (pwdargs != NULL) {
                          replPasswd = strVal(&pwdargs->val);
                          }
                          }
                          }
                          }
                          if (dinherit != NULL)
                          inherit = intVal(dinherit->arg);
                          if (dcreaterole != NULL)
                          createrole = intVal(dcreaterole->arg);
                          . . .
                          /* 查看要修改的角色是否存在,不存在则提示报错 */
                          Relation pg_authid_rel = heap_open(AuthIdRelationId, RowExclusiveLock);

                          HeapTuple tuple = SearchSysCache1(AUTHNAME, PointerGetDatum(stmt->role));
                          if (!HeapTupleIsValid(tuple)) {
                          str_reset(password);
                          str_reset(replPasswd);

                          if (!have_createrole_privilege())
                          ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("Permission denied.")));
                          else
                          ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("role \"%s\" does not exist", stmt->role)));
                          }
                          roleid = HeapTupleGetOid(tuple);
                          . . .
                          /* 检查是否有权限更改相应角色的属性,权限不足则提示报错 */
                          if (roleid == BOOTSTRAP_SUPERUSERID) {
                          if (!(issuper < 0 && inherit < 0 && createrole < 0 && createdb < 0 && canlogin < 0 && isreplication < 0 &&
                          isauditadmin < 0 && issystemadmin < 0 && isvcadmin < 0 && useft < 0 && dconnlimit == NULL &&
                          rolemembers == NULL && validBegin == NULL && validUntil == NULL && drespool == NULL &&
                          dparent == NULL && dnode_group == NULL && dspacelimit == NULL && dtmpspacelimit == NULL &&
                          dspillspacelimit == NULL)) {
                          str_reset(password);
                          str_reset(replPasswd);
                          ereport(ERROR,
                          (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                          errmsg("Permission denied to change privilege of the initial account.")));
                          }
                          }
                          if (dpassword != NULL && roleid == BOOTSTRAP_SUPERUSERID && GetUserId() != BOOTSTRAP_SUPERUSERID) {
                          str_reset(password);
                          str_reset(replPasswd);
                          ereport(ERROR,
                          (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                          errmsg("Permission denied to change password of the initial account.")));
                          }
                          . . .
                          } else if (!have_createrole_privilege()) {
                          if (!(inherit < 0 && createrole < 0 && createdb < 0 && canlogin < 0 && isreplication < 0 && isauditadmin < 0 &&
                          issystemadmin < 0 && isvcadmin < 0 && useft < 0 && dconnlimit == NULL && rolemembers == NULL &&
                          validBegin == NULL && validUntil == NULL && !*respool && !OidIsValid(parentid) && dnode_group == NULL &&
                          !spacelimit && !tmpspacelimit && !spillspacelimit &&
                          /* if not superuser or have createrole privilege, permission of lock and unlock is denied */
                          stmt->lockstatus == DO_NOTHING &&
                          /* if alter password, it will be handled below */
                          roleid == GetUserId()) ||
                          (roleid != GetUserId() && dpassword == NULL)) {
                          str_reset(password);
                          str_reset(replPasswd);
                          ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("Permission denied.")));
                          }
                          }
                          ...
                          /* 将要更改的角色属性值分别更新到新元组中,再将新元组替代旧元组存入系统表pg_authid中 */
                          if (issuper >= 0) {
                          new_record[Anum_pg_authid_rolsuper - 1] = BoolGetDatum(issuper > 0);
                          new_record_repl[Anum_pg_authid_rolsuper - 1] = true;

                          new_record[Anum_pg_authid_rolcatupdate - 1] = BoolGetDatum(issuper > 0);
                          new_record_repl[Anum_pg_authid_rolcatupdate - 1] = true;
                          }
                          if (inherit >= 0) {
                          new_record[Anum_pg_authid_rolinherit - 1] = BoolGetDatum(inherit > 0);
                          new_record_repl[Anum_pg_authid_rolinherit - 1] = true;
                          }
                          . . .
                          HeapTuple new_tuple = heap_modify_tuple(tuple, pg_authid_dsc, new_record, new_record_nulls, new_record_repl);
                          simple_heap_update(pg_authid_rel, &tuple->t_self, new_tuple);

                          CatalogUpdateIndexes(pg_authid_rel, new_tuple);
                          . . .
                          /* 判断成员关系,增加或删除成员 */
                          if (stmt->action == 1)
                          AddRoleMems(stmt->role, roleid, rolemembers, roleNamesToIds(rolemembers), GetUserId(), false);
                          else if (stmt->action == -1) /* drop members FROM role */
                          DelRoleMems(stmt->role, roleid, rolemembers, roleNamesToIds(rolemembers), false);

                          . . .
                          heap_close(pg_authid_rel, NoLock);
                          }

                          2. 删除角色

                          如果要删除一个角色,可以使用SQL命令DROP ROLE。角色的删除是通过调用DropRole函数来实现的,该函数只有一个类型为DropRoleStmt结构的参数。相关代码如下:

                            typedef struct DropRoleStmt {
                            NodeTagtype;
                            List*roles; /* 要删除的角色列表 */
                            boolmissing_ok; /* 判断角色是否存在 */
                            boolis_user; /* 要删除的是角色还是用户 */
                            boolinherit_from_parent; /* 是否继承自父角色*/
                            DropBehavior behavior; /* 是否级联删除依赖对象 */
                            } DropRoleStmt;
                            删除角色的流程如图3所示

                            图3  openGauss角色删除流程图

                            角色删除的执行流程为首先判断当前操作者是否有权限执行该操作,若没有则报错退出;然后检查待删除的角色是否存在,若不存在,则根据missing_ok选择返回ERRORNOTICE提示信息;再通过扫描系统表pg_authidpg_auth_members,删除所有涉及待删除角色的元组执行;若behavior取值DROP_CASCADE,则级联删除该角色所拥有的所有数据库对象;最后删除该角色在系统表pg_auth_historypg_user_status中对应的信息具体的实现过程代码如下:

                              void DropRole(DropRoleStmt* stmt)
                              {
                              . . .
                              /* 检查执行者是否有权限删除角色 */
                              if (!have_createrole_privilege())
                              ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("Permission denied to drop role.")));
                              /* 循环处理要删除的角色 */
                              foreach (item, stmt->roles) {
                              . . .
                              /* 检查要删除的角色是否存在,若不存在则提示报错 */
                              HeapTuple tuple = SearchSysCache1(AUTHNAME, PointerGetDatum(role));
                              if (!HeapTupleIsValid(tuple)) {
                              if (!stmt->missing_ok) {
                              ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("role \"%s\" does not exist", role)));
                              } else {
                              ereport(NOTICE, (errmsg("role \"%s\" does not exist, skipping", role)));
                              }
                              continue;
                              }
                              . . .
                              /* 当前用户不允许删除 */
                              if (roleid == GetUserId())
                              ereport(ERROR, (errcode(ERRCODE_OBJECT_IN_USE), errmsg("current user cannot be dropped")));
                              if (roleid == GetOuterUserId())
                              ereport(ERROR, (errcode(ERRCODE_OBJECT_IN_USE), errmsg("current user cannot be dropped")));
                              if (roleid == GetSessionUserId())
                              ereport(ERROR, (errcode(ERRCODE_OBJECT_IN_USE), errmsg("session user cannot be dropped")));
                              /* 校验执行者和被删除角色的权限,如系统管理员才有权限删除其他系统管理员 */
                              if((((Form_pg_authid)GETSTRUCT(tuple))->rolsuper|| ((Form_pg_authid)GETSTRUCT(tuple))->rolsystemadmin) &&
                              !isRelSuperuser())
                              ereport(ERROR,(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("Permission denied.")));
                              if ((((Form_pg_authid)GETSTRUCT(tuple))->rolauditadmin) &&
                              g_instance.attr.attr_security.enablePrivilegesSeparate && !isRelSuperuser())
                              ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("Permission denied.")));
                              . . .
                              /* 针对CASCADE(级联)的情况,删除该角色拥有的对象 */
                              if (stmt->behavior == DROP_CASCADE) {
                              char* user = NULL;
                              CancelQuery(role);
                              user = (char*)palloc(sizeof(char) * strlen(role) + 1);
                              errno_t errorno = strncpy_s(user, strlen(role) + 1, role, strlen(role));
                              securec_check(errorno, "\0", "\0");
                              drop_objectstmt.behavior = stmt->behavior;
                              drop_objectstmt.type = T_DropOwnedStmt;
                              drop_objectstmt.roles = list_make1(makeString(user));

                              DropOwnedObjects(&drop_objectstmt);
                              list_free_deep(drop_objectstmt.roles);
                              }

                              /* 检查是否有对象依赖于该角色,若还存在依赖,则提示报错 */
                              if (checkSharedDependencies(AuthIdRelationId, roleid, &detail, &detail_log))
                              ereport(ERROR,
                              (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
                              errmsg("role \"%s\" cannot be dropped because some objects depend on it", role),
                              errdetail_internal("%s", detail),
                              errdetail_log("%s", detail_log)));
                              /* 从相关系统表中删除涉及待删除角色的元组 */
                              simple_heap_delete(pg_authid_rel, &tuple->t_self);
                              . . .
                              while (HeapTupleIsValid(tmp_tuple = systable_getnext(sscan))) {
                              simple_heap_delete(pg_auth_members_rel, &tmp_tuple->t_self);
                              }

                              systable_endscan(sscan);
                              DropAuthHistory(roleid);
                              DropUserStatus(roleid);
                              DeleteSharedComments(roleid, AuthIdRelationId);
                              DeleteSharedSecurityLabel(roleid, AuthIdRelationId);
                              DropSetting(InvalidOid, roleid);
                              . . .
                              heap_close(pg_auth_members_rel, NoLock);
                              heap_close(pg_authid_rel, NoLock);
                              }

                              3. 授予和回收角色

                              如果要授予或回收角色的成员关系,可以使用SQL命令GRANT/REVOKE。如果声明了WITH ADMIN OPTION选项,那么被加入的成员角色还可以将其他角色加入到父角色中。角色的授予或回收通过调用GrantRole函数来实现,该函数只有一个类型为GrantRoleStmt结构的参数。相关代码如下:

                                typedef struct GrantRoleStmt {
                                NodeTag type;
                                List* granted_roles;/* 被授予或回收的角色集合 */
                                List* grantee_roles;/* 从granted_roles中增加或删除的角色集合 */
                                Bool is_grant;/* true代表授权,false代表回收 */
                                Bool admin_opt;/* 是否带有with admin option选项 */
                                char* grantor;/* 授权者 */
                                Drop Behaviorbehavior;/* 是否级联回收角色 */
                                } GrantRoleStmt;

                                授予角色时,grantee_roles中的角色将被添加到granted_roles,通过调用函数AddRoleMems实现;回收角色时,将grantee_roles中的角色从granted_roles中删除,通过调用函数DelRoleMems实现。

                                函数AddRoleMems的实现流程如图4所示

                                图4  openGauss增加用户成员流程图

                                函数AddRoleMems的具体实现代码如下,其中

                                1rolenameroleid分别表示要被加入成员的角色的名称和OID
                                2memberNamesmemberIds分别是要添加的角色名称和OID的列表
                                3grantorId表示授权者的OID
                                4admin_opt表示是否带有with admin option选项。
                                  static void AddRoleMems(
                                  const char* rolename, Oid roleid, const List* memberNames, List* memberIds, Oid grantorId, bool admin_opt)
                                  {
                                  . . .
                                  /* 校验执行者的权限 */
                                  if (superuser_arg(roleid)) {
                                  if (!isRelSuperuser())
                                  ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("Permission denied.")));
                                  . . .
                                  }
                                  . . .
                                  if (grantorId != GetUserId() && !superuser())
                                  ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be system admin to set grantor")));
                                  /* 循环处理要添加的角色 */
                                  pg_authmem_rel = heap_open(AuthMemRelationId, RowExclusiveLock);
                                  pg_authmem_dsc = RelationGetDescr(pg_authmem_rel);

                                  forboth(nameitem, memberNames, iditem, memberIds)
                                  {
                                  /* 针对角色和成员信息创建pg_auth_members元组,再将新元组插入到系统表中 */
                                  . . .
                                  new_record[Anum_pg_auth_members_roleid - 1] = ObjectIdGetDatum(roleid);
                                  new_record[Anum_pg_auth_members_member -1] = ObjectIdGetDatum(memberid);
                                  new_record[Anum_pg_auth_members_grantor - 1] = ObjectIdGetDatum(grantorId);
                                  new_record[Anum_pg_auth_members_admin_option - 1] = BoolGetDatum(admin_opt);

                                  if (HeapTupleIsValid(authmem_tuple)) {
                                  new_record_repl[Anum_pg_auth_members_grantor - 1] = true;
                                  new_record_repl[Anum_pg_auth_members_admin_option - 1] = true;
                                  tuple = heap_modify_tuple(authmem_tuple, pg_authmem_dsc, new_record, new_record_nulls, new_record_repl);
                                  simple_heap_update(pg_authmem_rel, &tuple->t_self, tuple);
                                  CatalogUpdateIndexes(pg_authmem_rel, tuple);
                                  ReleaseSysCache(authmem_tuple);
                                  } else {
                                  tuple = heap_form_tuple(pg_authmem_dsc, new_record, new_record_nulls);
                                  (void)simple_heap_insert(pg_authmem_rel, tuple);
                                  CatalogUpdateIndexes(pg_authmem_rel, tuple);
                                  }
                                  }
                                  . . .
                                  heap_close(pg_authmem_rel, NoLock);
                                  }
                                  函数DelRoleMems的实现过程类似。首先对执行者的相关权限进行校验,然后循环处理要删除的角色,删除系统表pg_auth_member中相关的元组。
                                  以上内容为安全管理源码解析中角色管理的相关内容,下篇图文将分享“对象权限管理”的精彩内容,敬请期待!
                                  - END -





                                  Gauss松鼠会
                                  汇集数据库从业人员及爱好者
                                  互助解决问题 共建数据库技术交流圈

                                  文章转载自Gauss松鼠会,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                                  评论