设计
如果要限制用户在某一天的某时间段可以登录,某时间段不可以登录,在此做了一种简单的实现,通过pg_hba.conf文件配置时间段,示例如下:
# TYPE DATABASE USER ADDRESS TIME METHOD
# "local" is for Unix domain socket connections only
local all all trust
# IPv4 local connections:
host all postgres,user1 127.0.0.1/32 "MonTueWedThuFri|08:00:00-10:00:00,SatSun|10:00:00-16:00:00" md5
# IPv6 local connections:
host all all ::1/128 "MonTueWedThuFri|08:00:00-20:00:00,SatSun|10:00:00-16:00:00" trust
# Allow replication connections from localhost, by a user with the
# replication privilege.
local replication all trust
host replication all 127.0.0.1/32 "MonTueWedThuFri|08:00:00-20:00:00,SatSun|10:00:00-16:00:00" trust
host replication all ::1/128 "MonTueWedThuFri|08:00:00-20:00:00,SatSun|10:00:00-16:00:00" trust
如上:
1、增加TIME字段设置时间段配置;
2、针对本地登录(local)不做设置;
3、时间段规则解析:
1)每一条配置可以设置多组时间段,以","分割;
2)每一组时间段可以对应多天,以"|"分割,比如上例中的“MonTueWedThuFri|08:00:00-10:00:00”代表周一到周五五天每天的8:00到10点都可以登录;
3)每一组内只能有一个时间段,不能出现多个时间段,如果要配置某一天多个时间段可以登录,可以配置多组,每一组代表一个时间段。假如周一周二两天允许上午和下午两个时间段登录,可以这么配置:
"MonTue|09:00:00-11:00:00,MonTue|13:00:00-15:00:00"
4)时间段设置以周为单位,时间要具体到秒,以英文缩写(前三个字母,第一个大写,第二个第三个小写)代表每周的某一天。
实现
基于postgresql14.7的源代码实现如下:
1、解析:parse_hba_line函数逐条解析pg_hba.conf中的配置,在此新增对TIME字段的解析并保存,解析的同时需要检查设置是否符合规则或错误,检查代码如下:
static int timerange_check_day(char *days) {
char day[4];
day[3] = '\0';
for(int i = 0;i < strlen(days); i += 3){
memcpy(day, days + i, 3);
if (strcmp(day, "Mon") != 0 && strcmp(day, "Tue") != 0 && strcmp(day, "Wed") != 0 &&
strcmp(day, "Thu") != 0 && strcmp(day, "Fri") != 0 && strcmp(day, "Sat") != 0 &&
strcmp(day, "Sun") != 0) {
return 0;
}
}
return 1;
}
static int timerange_check_time(char *time) {
int h1, m1, s1, h2, m2, s2;
if (sscanf(time, "%02d:%02d:%02d-%02d:%02d:%02d", &h1, &m1, &s1, &h2, &m2, &s2) != 6) {
return 0;
}
if (h1 < 0 || h1 > 23 || m1 < 0 || m1 > 59 || s1 < 0 || s1 > 59 ||
h2 < 0 || h2 > 23 || m2 < 0 || m2 > 59 || s2 < 0 || s2 > 59) {
return 0;
}
return 1;
}
static int timerange_check_group(char *group) {
char *saveptr1 = NULL;
char *saveptr2 = NULL;
char *token = NULL;
char *days = NULL;
char *time = NULL;
char *day = NULL;
int day_count = 0;
token = strtok_r(group, "|", &saveptr1);
if (token == NULL) {
return 0;
}
days = token;
token = strtok_r(NULL, "|", &saveptr1);
if (token == NULL) {
return 0;
}
time = token;
// 检查 days 部分
day = strtok_r(days, "", &saveptr2);
while (day != NULL) {
if (!timerange_check_day(day)) {
return 0;
}
day_count++;
day = strtok_r(NULL, "", &saveptr2);
}
if (day_count < 1 || day_count > 7) {
return 0;
}
// 检查 time 部分
if (!timerange_check_time(time)) {
return 0;
}
return 1;
}
static int timerange_check_rule(char *rule) {
char *saveptr = NULL;
char *token = strtok_r(rule, ",", &saveptr);
while (token != NULL) {
if (!timerange_check_group(token)) {
return 0;
}
token = strtok_r(NULL, ",", &saveptr);
}
return 1;
}
static bool timerange_time_check(char *timestr) {
char *rule = pstrdup(timestr);
if (timerange_check_rule(rule))
return true;
return false;
}
2、登录检查:客户端登录时,在函数check_hba中新增时间检查,检查当前日期和时间点是否在配置的时间段范围内,时间点检查函数如下:
typedef struct {
char days[100];
char start_time[10];
char end_time[10];
} TimePeriod;
static void timerange_getCurrentTime(char *current_time) {
time_t now = time(NULL);
struct tm *t = localtime(&now);
sprintf(current_time, "%02d:%02d:%02d", t->tm_hour, t->tm_min, t->tm_sec);
}
static void timerange_getCurrentDate(char *current_date) {
time_t now = time(NULL);
struct tm *t = localtime(&now);
sprintf(current_date, "%d-%02d-%02d", t->tm_year + 1900, t->tm_mon + 1, t->tm_mday);
}
static int timerange_getCurrentWeekday() {
time_t now = time(NULL);
struct tm *t = localtime(&now);
return t->tm_wday;
}
static int timerange_compareTime(const char *time1, const char *time2) {
int h1, m1, s1, h2, m2, s2;
sscanf(time1, "%d:%d:%d", &h1, &m1, &s1);
sscanf(time2, "%d:%d:%d", &h2, &m2, &s2);
if (h1 < h2) {
return -1;
} else if (h1 > h2) {
return 1;
} else {
if (m1 < m2) {
return -1;
} else if (m1 > m2) {
return 1;
} else {
if (s1 < s2) {
return -1;
} else if (s1 > s2) {
return 1;
} else {
return 0;
}
}
}
}
static int timerange_checkTimePeriod(TimePeriod *period, char *current_time) {
int start_comp = timerange_compareTime(period->start_time, current_time);
int end_comp = timerange_compareTime(period->end_time, current_time);
return start_comp <= 0 && end_comp >= 0;
}
static int timerange_checkDay(char *days, int current_weekday) {
char weekdays[][4] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
if (current_weekday >= 0 && current_weekday < 7 && (strstr(days, weekdays[current_weekday]) != NULL)) {
return 1;
}
return 0;
}
static bool check_timerange(char *timestr) {
char *token = NULL;
char *rules = pstrdup(timestr);
char current_time[10];
char current_date[20];
int current_weekday = timerange_getCurrentWeekday();
char *saveptr1, *saveptr2, *saveptr3;
timerange_getCurrentTime(current_time);
timerange_getCurrentDate(current_date);
token = strtok_r(rules, ",", &saveptr1);
while (token != NULL) {
char *days_time = NULL;
char *time_range = NULL;
char *start_time = NULL;
char *end_time = NULL;
TimePeriod period;
days_time = strtok_r(token, "|", &saveptr2);
strcpy(period.days, days_time);
time_range = strtok_r(NULL, "|", &saveptr2);
start_time = strtok_r(time_range, "-", &saveptr3);
strcpy(period.start_time, start_time);
end_time = strtok_r(NULL, "-", &saveptr3);
strcpy(period.end_time, end_time);
if (timerange_checkDay(period.days, current_weekday) && timerange_checkTimePeriod(&period, current_time))
return true;
token = strtok_r(NULL, ",", &saveptr1);
}
return false;
}
至此,基于时间的登录设置功能已实现,如果考虑到视图pg_hba_file_rules的正确展现hba文件的设置,还需要做如下修改:
3、修改视图pg_hba_file_rules,新增时间范围字段:
{ oid => '3401', descr => 'show pg_hba.conf rules',
proname => 'pg_hba_file_rules', prorows => '1000', proretset => 't',
provolatile => 'v', prorettype => 'record', proargtypes => '',
proallargtypes => '{int4,text,_text,_text,text,text,text,text,_text,text}',
proargmodes => '{o,o,o,o,o,o,o,o,o,o}',
proargnames => '{line_number,type,database,user_name,address,netmask,timerange,auth_method,options,error}',
prosrc => 'pg_hba_file_rules' },
4、修改一个宏原为9改为10,因为上面的视图原来是9个字段,现在新增一个字段变为10个:
#define NUM_PG_HBA_FILE_RULES_ATTS 10
5、在函数fill_hba_line函数中新增对新增字段timerange的填充。
6、最后最好修改src/backend/libpq/pg_hba.conf.sample,在模板文件中配置上默认的时间段。
完整的时间规则检查和时间点检查测试验证用例详见:
【免费】基于规则“SunThu-00:02:00-00:03:00,Mon-16:59:00-20:00:00”时间范围检查(C语言)资源-CSDN文库
思考
如何才能更灵活、更清晰明了的配置时间范围?
可不可以通过插件方式实现?
......
标签:基于,PostgreSQL,timerange,00,认证,char,time,return,NULL From: https://blog.csdn.net/weixin_38700215/article/details/140151625