1 module hunt.framework.config.AuthUserConfig;
2 
3 import hunt.framework.Init;
4 
5 import std.algorithm;
6 import std.array;
7 import std.conv;
8 import std.file;
9 import std.path;
10 import std.stdio;
11 import std.range;
12 import std.string;
13 
14 import hunt.logging;
15 
16 
17 /**
18  * Convert the permission format from Hunt to Shiro, which means that 
19  * all the '.' will be replaced with the ':'.
20  */
21 static string toShiroPermissions(string value) {
22     return value.replace('.', ':');
23 }
24 
25 /**
26  * 
27  */
28 class AuthUserConfig {
29     static class User {
30         string name;
31         string password;
32         string[] roles;
33 
34         override string toString() {
35             return "name: " ~ name ~ ", role: " ~ roles.to!string();
36         }
37     }
38 
39     static class Role {
40         string name;
41 
42         /**
43          * Examples:
44          *  printer:query,print:lp7200
45          * 
46          * See_also:
47          *  https://shiro.apache.org/permissions.html
48          */
49         string[] permissions;
50 
51         override string toString() {
52             return "name: " ~ name ~ ", permissions: " ~ permissions.to!string();
53         }
54     }
55 
56     User[] users;
57 
58     Role[] roles;
59 
60     static AuthUserConfig load(string userConfigFile, string roleConfigFile) {
61         version(HUNT_DEBUG) {
62             tracef("Loading users from %s", userConfigFile);
63             tracef("Loading roles from %s", roleConfigFile);
64         }
65         
66         AuthUserConfig config = new AuthUserConfig();
67 
68         if (exists(userConfigFile)) {
69             File f = File(userConfigFile, "r");
70             scope(exit) f.close();
71 
72             string line;
73             while((line = f.readln()) !is null) {
74                 line = line.strip();
75 
76                 if(line.empty) continue;
77 
78                 if (line[0] == '#' || line[0] == ';')
79                     continue;
80 
81                 string[] parts = split(line, " ");
82                 if(parts.length < 2) continue;
83 
84                 string fieldValue;
85                 string password;
86                 string roles;
87 
88                 int fieldIndex = 1;
89                 foreach(string v; parts[1..$]) {
90                     fieldValue = v.strip();
91                     if(fieldValue.empty) continue;
92 
93                     if(fieldIndex == 1) password = fieldValue;
94                     if(fieldIndex == 2) roles = fieldValue;
95 
96                     fieldIndex++;
97                     if(fieldIndex > 2) break;
98                 }
99 
100                 User user = new User();
101                 user.name = parts[0].strip();
102                 user.password = password;
103                 user.roles = roles.split("|");
104 
105                 config.users ~= user;
106             }
107         }
108 
109         
110         if (exists(roleConfigFile)) {
111             File f = File(roleConfigFile, "r");
112             scope(exit) f.close();
113 
114             string line;
115             while((line = f.readln()) !is null) {
116                 line = line.strip();
117 
118                 if(line.empty) continue;
119 
120                 if (line[0] == '#' || line[0] == ';')
121                     continue;
122 
123                 string[] parts = split(line, " ");
124                 if(parts.length < 2) continue;
125 
126                 Role role = new Role();
127                 role.name = parts[0].strip();
128 
129                 string permissions;
130                 foreach(string v; parts[1..$]) {
131                     permissions = v.strip();
132                     if(!permissions.empty) break;
133                 }
134 
135                 role.permissions = permissions.split("|").map!(p => p.strip().toShiroPermissions()).array;
136                 config.roles ~= role;
137             }            
138         }
139 
140         return config;
141 
142     }
143 }