# 安全

# 导入

通过如下方式导入安全api

import security from "svr-api/security";

# 方法

/**
* 此文件定义一些安全相关的全局函数。
* 
*/

/**
 * 获取当前用户是否已经登录
 */
export function isLogin(): boolean;

/**
 * 使当前用户退出登录
 */
export function logout():void;

/**
 * 获取当前登录用户的信息
 * 注意,在未登录的情况下调用本方法会返回null
 */
export function getCurrentUser(): CurrentUser;

/**
 * 使用用户名和密码进行登录
 * @param userId 用户名
 * @param password 密码
 * @param userDirectory 用户类型,`sys`代表系统用户,`external`代表外部用户,默认为`sys`
 * @return 如果登录失败,返回false;登录成功返回true
 */
export function login(userId: string, password: string, userDirectory?: string): boolean;

/**
 * 无密码登录(用户表中还是需要有对应的用户存在)。
 * 
 * 1. 如果当前用户已经是登录状态,那么本函数会先判断当前用户名是否和userId参数一致,如果一致,则什么都不做;如果不一致,则会先注销登录,再用新的用户名登录。
 * 2. 如果想总是先注销登录(不管当前登录用户的用户名是否跟userId一致),再用新用户名登录,那么请先调用security.logout(),再调用本方法
 * 3. 如果想在当前用户已经登录时不用新的用户名登录,那么要这么写: !security.isLogin() && security.loginWithoutPassword(userId);
 * 
 * @param userId 
 * @param  userDirectory  用户类型,`sys`代表系统用户,`external`代表外部用户,默认为`sys`
 * @param loginType 所使用的登录方式,不传代表未知
 * 		系统自带的可用值有:
 * 		- "password" : 使用账号+密码或者手机号+密码 登录
 * 		- "pw&sms" : 使用账号+密码登录后,再次使用了手机验证码登录
 * 		- "sms": 使用手机验证码登录
 * 		- "qrcode": 扫码二维码登录
 * 		- "sso": 通过单点登录直接从第三方系统跳转登录
 * 		- "noneuser": 通过 {@link loginWithoutUser} 登录,系统不存储用户
 * @return 如果登录失败,返回false;登录成功返回true
 */
export function loginWithoutPassword(userId: string, userDirectory?: string, loginType?: string): boolean;

/**
 * 用一个临时的用户登录(用户表中不需要有对应的用户存在,也不验证它的密码)。
 * 
 * 使用场景:
 * 1. 适用于用户登录量很大,系统不需要在数据库专门记录用户的信息,通过内存中的信息足够“标示”一个用户,用户也只是只是临时登录一下使用系统
 * 2. 例如,系统需要和某app做接口,在某app中加一个入口模块,点击后它会通过url中传递一个"token",系统后台可以根据token获取用户信息并登录,
 * 	用户量很大,比如1亿
 * 
 * @param user 要登录的用户的信息
 * @param groups 要登录的用户所属的用户组,如果传递了,那么将不读取系统默认的用户组成员表
 * @param userDirectory 用户目录,`sys`代表系统用户,`external`代表外部用户,默认为`sys`
 */
export function loginWithoutUser(user: UserInfo, groups: string[], userDirectory?: string): boolean;

/**
 * 在当前线程中模拟一个web线程的登录
 *
 * 使用场景:
 * 1. 需要定时执行一个任务,任务需要导出一个报表为excel文件,然后将文件作为消息发送出去:
 * 		考虑到,消息可能发送给不同用户,不同用户看到的报表的数据范围也是不一样的,通过修改当前执行任务的登录上下文,可以切换用户,然后根据用户被设置的权限来控制导出数据的范围
 * 
 * 常规使用写法为:
 * ```code
 *   import security from 'svr-api/security'
 * 
 *   let mockUserId = 'mockUserId';//待用来模拟登录的用户id
 *   let currentUserId = security.getCurrentUser().userInfo.userId;// 获取到当前线程中登录的用户id
 *   security.mockLoginSessionBean(mockUserId);
 *   // xxx 处理业务
 *   security.mockLoginSessionBean(currentUserId);//将登录还原到处理任务之前
 *   // xxx 处理其他业务
 * ```
 * 
 * @param userId 用来登录的用户的ID,请务必保证用户在系统中存在
 */
export function mockLoginSessionBean(userId: string): void;

/**
 * 清理当前线程中的登录信息,与{@link mockLoginSessionBean}相对
 *
 * 说明:执行本方法后,请务必要保证后续继续执行的代码不会使用到登录信息
 */
export function cleanLoginSessionBean(): void;

/**
 * 根据用户ID获取一个用户对象
 * 
 * @param id 用户id,大小写敏感
 * @param  userDirectory  用户类型,`sys`代表系统用户,`external`代表外部用户,默认为`sys`
 * @return 用户对象,null表示不存在
 */
export function getUser(id: string, userDirectory?: string): UserInfo;

/**
 * 根据ID批量获取用户信息
 * 
 *  使用场景:
 *  	1. 完成构建工作流后,工作流配置文件中存储有已经选定的审批人、填报人的信息,为保证再次查看或编辑工作流时,所展示审批人和填报人信息准确,
 *  		工作流配置文件中所存储的用户信息仅为USER_ID,再次加载工作流的时候查询获取最新的完整用户信息
 * @param userIds 用户id集合
 * @param userDirectory 用户目录,sys代表系统用户,external代表外部用户,默认为sys
 * @return 都不存在返回空数组 
 */
export function getUsers(userIds: Array<string>, userDirectory?: string): Array<UserInfo>;

/**
 * 列出所有的用户
 * 
 * @param userDirectory 用户目录,sys代表系统用户,external代表外部用户,默认为sys
 * @return
 */
export function getAllUsers(userDirectory?: string): Array<UserInfo>;

/**
 * 添加新用户
 * - 添加后会刷新该用户被匹配到德用户组列表
 * - `user`参数中的属性名为数据库字段的驼峰式命名方式,比如:数据库字段  USER_ID ,这里需要改成用 userId
 * 
 * @param item
 * @param userDirectory 用户目录,sys代表系统用户,external代表外部用户,默认为sys
 * @return
 */
export function addUser(user: UserInfo, userDirectory?: string): void;

/**
 * 删除用户
 * 
 * 同步删除用户组关联、用户状态信息及为用户特殊分配的权限条目
 * @param userDirectory 用户目录,sys代表系统用户,external代表外部用户,默认为sys
 * @param userId
 */
export function deleteUser(id: string, userDirectory?: string): void;

/**
 * 更新用户信息。
 *
 * 规定:
 * 1. 这个接口将全量更新用户表属性,如果`user`中不存在或者属性指定为null,都将清空属性
 * 2. `user`参数中的属性名为数据库字段的驼峰式命名方式,比如:数据库字段  USER_ID ,这里需要改成用 userId
 * 3. 属性更改后会刷新该用户被匹配到德用户组列表
 * @param user
 * @param userDirectory 用户目录,sys代表系统用户,external代表外部用户,默认为sys
 */
export function updateUser(user: UserInfo, userDirectory?: string): void;

/**
 * 刷新用户被自动匹配到的用户组
 * 
 * - 用于在通过sql或者dwapi直接修改或者添加用户的时候,来刷新用户所在用户组列表。
 * - `addUser` 和 `updateUser` 都会调用此方法
 * - 外部用户不能被分配用户组,也就不能调用此方法刷新
 * @param userId 用户id
 * @param newUser 是否为新添加用户
 */
export function refreshAutoMatchUserGroups(userId: string, newUser?: boolean): void

/**
 * 获取对应的部门信息 
 * @param id 部门ID
 * @return
 */
export function getDept(id: string): DeptInfo;

/**
 * 获取用户所属的所有用户组
 * @param id 用户id
 * @param userDirectory 用户目录,sys代表系统用户,external代表外部用户,默认为sys
 */
export function getGroupsOfUser(userId: string, userDirectory?: string): UserGroupInfo[];

/**
 * 获取子部门列表
 * @param deptId 
 */
export function getChildrenDepts(deptId: string): DeptInfo[];

/**
 * 校验登录所使用的 access_token 是否有效
 * @param access_token 
 */
export function checkAccessToken(access_token: string): {
	/** 授权用户的用户信息 */
	user: UserInfo;
	/**
	 * 是否 access_token 有效
	 */
	valid: boolean;
	/**
	 * access_token 无效的时候,给出的错误码
	 */
	errorCode: string;
	/**
	 * access_token 无效的时候给出的文本提示信息
	 */
	errorMessage:string;
}

/**
 * 向某个手机号码或者邮箱发送一个验证码。
 * 
 * 1. 发送短信或者邮件都需要一个“模版”(大部分短信平台都需要这个,为了杜绝垃圾短信)
 * 2. 参数`templateCode`用于指定模版,验证码会作为一个参数传递给模版,参数名为`code`,验证码由系统自动产生
 * 3. 不能太频繁的对同一个手机发送短信,系统默认 1分钟(具体可以在系统设置中修改)内不能重复发送短信
 * 4. 同一个登录会话(根据JSessionID判断)也不能太频繁发送短信,限制时间和第三条一致
 * 5. 验证手机号码并不要求用户一定登录(比如通过手机短信找回密码)
 * 
 * @param args.phone 手机号
 * @param args.email 邮箱 ,和手机号必须要传一个
 * @param args.useType 表示此验证码的用途,可以为空,验证的时候可以使用,用于附加的判断在验证验证码的时候是否符合实际期望用途
 * @param args.templateCode 用于指定短信模版,验证码会作为一个参数传递给短信模版
 * @param args.verifyCodeParamName 验证码参数在消息模板中对应的参数名称,如果不设置,默认使用 `code`
 */
export function sendVerificationCode(args: { phone?: string; email?: string; useType?: string, templateCode?: string, verifyCodeParamName?: string }): {
	/** 是否发送成功 */
	result: boolean;
	/** 发送失败后的错误码 */
	errorCode?: string;
	/** 发送失败后的文本提示 */
	message?: string;
};

/**
 * 验证用户输入的验证码是否正确,以此判断手机是否是当前登录用户的。多用于用户在上一个页面已经输入过了验证码验证过了手机。
 * 
 * 1. 验证手机号码并不要求用户一定登录(比如通过手机短信找回密码)
 * 2. 如果先后多次发送过验证码,那么只验证最后一次发送的
 * 3. 如果连续验证失败,将锁定一定时间(系统设置可配置)不允许验证
 * 4. 手机短信/邮件验证有时效性限制,用户必须在指定的时间内验证(通常是10分钟),否则验证码失效
 * 5. 手机短信/邮件验证有时效性限制,用户必须在当前会话中验证,即发送验证码的会话和验证的是一个会话
 * 
 * @param args.phone 手机号
 * @param args.email 邮箱,和手机号必须要传一个
 * @param args.code 验证码
 * @param args.userType 使用途径
 */
export function checkVerificationCode(args: { phone?: string; email?:string; code: string; useType?: string }): {
	/** 校验是否成功 */
	result: boolean;

	/** 错误码 */
	errorCode: string;

	/** 错误信息 */
	message: string;
};

/**
 * 使用用户id生成登录token。如果没有对应用户,则返回null。
 * @param userId 
 * @param userDirectory 
 * @returns
 */
export function createAccessToken(userId: string, userDirectory?: string): string | null;

/**
 * 根据系统设置的用户密码加密算法加密密码
 * @param password 密码
 * @returns 加密后的内容
 */
export function encryptUserPassword(password: string): string;

/**
 * 对比输入的明文密码是否与数据库记录的密码一致
 * @param password 明文密码
 * @param encryptedPassword 加密后的用户信息
 * @param salt 盐字段
 * @returns
 */
export function compareUserPassword(plainPassword: string, encryptedPassword: string, salt?: string): string;

# 对象

/**
 * 此文件定义一些安全相关的数据类型。
 * 
 */


/**
 * 消息扩展点实现者在发送消息时所接受到的参数
 */
declare interface SendMessageArgs {
	//消息的标题,可选,比如短信消息就不需要标题
	subject?: string,
	//消息体
	content: string,
	//目标用户,根据参数中的用户信息,实现者可以取得用户的邮件、电话等信息
	users: Array<UserInfo>,
}

/**
 * 当前用户对象
 */
declare interface CurrentUser {

    /**
     * 是否为管理员
     */
	readonly isAdmin: boolean;

    /**
     * 用户信息
     */
	userInfo: UserInfo;

	/**
	 * 获取当前用户所在用户组信息
	 */
	getGroups(): Array<UserGroupInfo>;

    /**
     * 获取用户的权限列表。往往用于在用户登录成功后运行时修改用户的权限信息,让用户根据特定的条件拥有某些动态权限。
     * 
     * @returns 返回的权限列表对象可以进行修改,修改不会马上生效,必须调用`setPermissions()`函数才会生效。
     */
	getPermissions(): PermissionsBuffer;

    /**
     * 重新从权限表加载用户的权限。 说明,执行之后用户的权限将会被重置为跟数据库一致。
     */
	reloadPermissions(): void;

    /**
     * 重新设置当前登录用户的运行时权限。
     *
     * @param permissions 要设置的权限列表,会覆盖掉用户当前的所有权限信息,通常是根据`getPermissions()`函数返回的结果进行修改后再调用
     *  此函数进行设置。
     */
	setPermissions(permissions: Array<PermissionItemBuffer> | PermissionsBuffer): void;

	/**
	 * 用于获取用户存储在后端的一个数据。
	 * 
	 * 有时候二次开发需要一个能按用户存储数据的接口,数据存储在后端,下次登录还可以使用到。
	 * 
	 * @param key 对应的key,长度不能超过256,不能为空,大小写敏感,不同的用户key不会冲突。
	 */
	getStorageValue(key: string): Promise<any>;

	/**
	 * 用途见{@link getStorageValue()}函数的注释。
	 * 
	 * @param key 
	 * @param value 要存储的数据,任意类型都可以,长度太大(超过1k)时后端会存储到clob中去。
	 */
	saveStorageValue(key: string, value: any): Promise<void>;

	/**
	 * 注销登录
	 */
	logout(): void;

	/**
	 * 检查当前登录用户是否有资源的操作权限
	 * @param path  资源路径
	 * @param operation 权限操作,如:查看("view-basic") 编辑("mgr-m") 导出("mgr-m-export") 保存("mgr-m-save")
	 * @param throwIfFail 没有对应权限的时候是否抛出ForbiddenException
	 * @returns 
	 */
	checkAllowed(path: string, operation: string, throwIfFail: boolean): boolean;

	/**
	 * 判断是否拥有一项资源的浏览权限(拥有任意权限或拥有子资源的任意权限或父资源的任意权限)
	 * @param path 资源路径
	 * @param throwIfFail 没有浏览权限的时候是否抛出ForbiddenException
	 * @return
	 */
	checkBrowsable(path: string, throwIfFail: boolean): boolean;
}

/**
 * 用户的权限列表,方便脚本添加运行时权限
 */
declare interface PermissionsBuffer {

    /**
     * 返回权限列表。通常用于传递给`CurrentUser.setPermissions()`函数。
     */
	permissions: Array<PermissionItemBuffer>;

    /**
     * 添加一条权限。
     * 
     * 需要注意的是: 新添加的权限会覆盖{@link this.permissions} 中记录与新添加权限所针对资源相同的权限
     * 
     * @param permissionItem 
     */
	addPermission(permissionItem: PermissionItemBuffer | Array<PermissionItemBuffer>): void;

    /**
     * 增加一条查看权限。如果是dashboard,那么能查看dashboard,以及重新计算、导出结果;如果是采集应用,那么能看数据,不能填报。
     * 
     * 需要注意的是: 新添加的权限会覆盖{@link this.permissions} 中记录与新添加权限所针对资源相同的权限
     * 
     * @param path 资源路径,可以是目录或文件,包含项目名称,如`/Demo/data/tables/XXXX.tbl`
     * @param dataRange 数据范围
     */
	addViewPermission(path: string, dataRange?: PermissionDataRange): void;

    /**
     * 增加一条管理权限。用户将能对元数据文件进行查看、增删改。等于拥有了文件的完全操作权限。
     * 示例:一般用户对个人目录有完全操作权限
     * 
     * 需要注意的是: 新添加的权限会覆盖{@link this.permissions} 中记录与新添加权限所针对资源相同的权限
     * 
     * @param path 资源路径,可以是目录或文件,包含项目名称,如`/Demo/data/tables/XXXX.tbl`
     * @param dataRange 数据范围
     */
	addMgrPermission(path: string, dataRange?: PermissionDataRange): void;

    /**
     * 增加一条填报权限。添加之后用户将可以填报采集应用、可以审核数据、可以上报数据,如果是上级单位,还可以汇总数据。
     * 说明,一般资源是采集应用时才需要调用本方法
     * 
     * 需要注意的是: 新添加的权限会覆盖{@link this.permissions} 中记录与新添加权限所针对资源相同的权限
     * 
     * @param path 资源路径,可以是目录或文件,包含项目名称,如`/Demo/data/tables/XXXX.tbl`
     * @param dataRange 
     */
	addFillPermission(path: string, dataRange?: PermissionDataRange): void;

    /**
     * 增加一条填报管理权限。添加之后用户可以在采集应用中做锁定、审批等操作
     * 
     * 需要注意的是: 新添加的权限会覆盖{@link this.permissions} 中记录与新添加权限所针对资源相同的权限
     * 
     * @param path 资源路径,可以是目录或文件,包含项目名称,如`/Demo/data/tables/XXXX.tbl`
     * @param dataRange 
     */
	addFillMgrPermission(path: string, dataRange?: PermissionDataRange): void;
}

/**
 * 一条权限信息
 */
declare interface PermissionItemBuffer {
    /**
     * 资源的路径
     */
	path: string;

    /**
     * 支持的操作列表
	 * 
	 * 键为操作代码,值为操作的状态, true 代表可用, false 代表禁用
     */
	operations: {[operation: string]: boolean };

    /**
     * 数据范围
     * 
     * 键为权限操作代码,值为可供操作的数据范围
     */
	dataRange?: { [operation: string]: PermissionDataRange };
}

/**
 * 数据范围限定
 */
declare type PermissionDataRange =
	/**
	 * 多个条件为 OR 关系 可组成: 可查看`湖北省-食品行业`和`湖南省-计算机行业`
	 */
	Array<{
		/**
		 * 键:
		 * 		项目设置中所存储可用做数据范围设置的维表ID, 见 project-settings.json.d.ts 中 DataRangeDims
		 *
		 * 		若指代的维表不能作为数据范围限定,则本条数据范围条件无效
		 *
		 * 值:	维度可用值
		 * 		说明:若维度为一个父子维,只要有父维度操作权限,就表明有子维度的操作权限
		 *
		 * 多个维度直接取值为 AND 关系:行业维度和地区维度可以组成: 查看 `湖北省-食品行业`
		 */
		[dim: string] : string[],
	}>
是否有帮助?
0条评论
评论