Skip to content

Commit 1b5f35b

Browse files
committed
added temporary token auth. and improved private net check
1 parent 15269a1 commit 1b5f35b

File tree

8 files changed

+258
-104
lines changed

8 files changed

+258
-104
lines changed

src/main/java/net/b07z/sepia/server/core/server/SparkJavaFw.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,8 @@ public static String returnNoAccess(Request request, Response response){
143143

144144
/**
145145
* Return simple access-restricted message with custom error code.
146+
* @param errorCode - code returned from authenticator - 1: communication error, 2: access denied, ...
147+
* @return
146148
*/
147149
public static String returnNoAccess(Request request, Response response, int errorCode){
148150
if (errorCode == 2){

src/main/java/net/b07z/sepia/server/core/server/Validate.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package net.b07z.sepia.server.core.server;
22

33
import net.b07z.sepia.server.core.tools.Debugger;
4+
import net.b07z.sepia.server.core.tools.Is;
45
import net.b07z.sepia.server.core.tools.Security;
56
import spark.Request;
67

@@ -42,12 +43,13 @@ public static String getLocalSignature(Request request, String challenge, long t
4243
*/
4344
public static boolean validateInternalCall(Request request, String submittedKey, String sharedServerKey) {
4445
//TODO: make this more secure by adding a white-list of IPs or something ...
45-
if (submittedKey != null && submittedKey.equals(sharedServerKey)){
46-
return true;
47-
}else if (submittedKey != null){
48-
Debugger.println("Invalid internal API call from " + request.ip(), 1);
46+
if (Is.nullOrEmpty(submittedKey) || Is.nullOrEmpty(sharedServerKey)){
4947
return false;
48+
}
49+
if (submittedKey.equals(sharedServerKey)){
50+
return true;
5051
}else{
52+
Debugger.println("Invalid internal API call from " + request.ip(), 1);
5153
return false;
5254
}
5355
}

src/main/java/net/b07z/sepia/server/core/tools/Security.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package net.b07z.sepia.server.core.tools;
22

33
import java.net.InetAddress;
4+
import java.net.UnknownHostException;
45
import java.nio.charset.StandardCharsets;
56
import java.security.MessageDigest;
67
import java.security.SecureRandom;
@@ -154,15 +155,16 @@ public static byte[] getAwsSignatureKey(String key, String dateStamp, String reg
154155
/**
155156
* Check if host (e.g. IP address) is from a private network (according to IANA IP range etc.).
156157
* @param host - host address with or without protocol (http..), port (..:8080) or path (.../index.html)
158+
* @throws UnknownHostException
157159
*/
158-
public static boolean isPrivateNetwork(String host) throws Exception {
160+
public static boolean isPrivateNetwork(String host) throws UnknownHostException {
159161
if (host == null || host.isEmpty()){
160162
new RuntimeException("Invalid host address!");
161163
}
162164
//clean-up host string
163165
host = host.trim().replaceAll("^http(s|)://", "");
164166
host = host.trim().replaceAll("/.*", "");
165-
if (host.matches("^localhost($|:\\d+$)")){
167+
if (host.matches("^localhost($|:\\d+$)|.*\\.local$")){
166168
return true;
167169
}
168170
if (host.contains(".")){

src/main/java/net/b07z/sepia/server/core/tools/StringTools.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package net.b07z.sepia.server.core.tools;
22

3+
import java.util.ArrayList;
4+
import java.util.List;
35
import java.util.regex.Matcher;
46
import java.util.regex.Pattern;
57

@@ -26,4 +28,19 @@ public static String findFirstRexEx(String input, String regEx){
2628
return "";
2729
}
2830
}
31+
32+
/**
33+
* Find all groups of matching regular expression or return empty string.
34+
* @param input - input to search in
35+
* @param regEx - regular expression to search for
36+
*/
37+
public static List<String> findAllRexEx(String input, String regEx){
38+
Pattern pattern = Pattern.compile(regEx);
39+
Matcher matcher = pattern.matcher(input);
40+
List<String> matches = new ArrayList<>();
41+
while (matcher.find()){
42+
matches.add(matcher.group(0));
43+
}
44+
return matches;
45+
}
2946
}

src/main/java/net/b07z/sepia/server/core/users/Account.java

Lines changed: 101 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import net.b07z.sepia.server.core.server.RequestParameters;
1515
import net.b07z.sepia.server.core.tools.ClassBuilder;
1616
import net.b07z.sepia.server.core.tools.Converters;
17+
import net.b07z.sepia.server.core.tools.Is;
1718
import net.b07z.sepia.server.core.tools.JSON;
1819
import net.b07z.sepia.server.core.tools.Security;
1920

@@ -45,6 +46,29 @@ public class Account {
4546
//more
4647
private Map<String, Object> info = new HashMap<>(); //info for everything that's not in the basics
4748

49+
/**
50+
* Sometimes you need a temporary token that is bound to certain account properties.
51+
* @param userId
52+
* @param userRoles
53+
* @param secret1
54+
* @param secret2
55+
* @param iterations
56+
* @return
57+
* @throws Exception
58+
*/
59+
public static String getTemporaryValidationToken(String userId, List<String> userRoles, String secret1, String secret2, int iterations)
60+
throws Exception{
61+
if (Is.nullOrEmpty(userId) || userRoles == null || Is.nullOrEmpty(secret1) || Is.nullOrEmpty(secret2) || iterations == 0){
62+
throw new RuntimeException("Invalid input for 'getTemporaryValidationToken'");
63+
}
64+
//build token
65+
return Security.bytearrayToHexString(Security.getEncryptedPassword(
66+
secret1.replaceAll("\\s", "").trim() + secret2.replaceAll("\\s", "").trim(),
67+
Security.getSha256((userId + userRoles.toString()).replaceAll("\\s", "").trim()),
68+
iterations, 32
69+
));
70+
}
71+
4872
/**
4973
* Return unique user ID.
5074
*/
@@ -156,7 +180,35 @@ public boolean authenticate(JSONObject requestJson){
156180
* @return true or false
157181
*/
158182
public boolean authenticate(RequestParameters params){
159-
//get parameters
183+
//client data?
184+
String clientInfo = params.getString("client");
185+
if (clientInfo == null || clientInfo.isEmpty()){
186+
clientInfo = ConfigDefaults.defaultClientInfo;
187+
}
188+
189+
//--- Temp. Token ---
190+
191+
//check for temporary token first
192+
JSONObject tToken = params.getJson("tToken");
193+
if (tToken != null){
194+
//call
195+
JSONObject authInfo = JSON.make(
196+
"tToken", tToken,
197+
"client", clientInfo
198+
);
199+
if (authService.authenticate(authInfo)){
200+
//get basic info
201+
copyBasicInfo();
202+
return true;
203+
}else{
204+
//fail
205+
return false;
206+
}
207+
}
208+
209+
//--- Default ---
210+
211+
//get username/password parameters
160212
String key = params.getString("KEY");
161213
if (key == null || key.isEmpty()){
162214
String guuid = params.getString("GUUID");
@@ -165,70 +217,74 @@ public boolean authenticate(RequestParameters params){
165217
key = guuid + ";" + Security.hashClientPassword(pwd);
166218
}
167219
}
168-
String client_info = params.getString("client");
169-
if (client_info == null || client_info.isEmpty()){
170-
client_info = ConfigDefaults.defaultClientInfo;
171-
}
172220
//check
173221
if (key != null && !key.isEmpty()){
174222
String[] up = key.split(";",2);
175223
if (up.length == 2){
176224
String username = up[0].toLowerCase();
177225
String password = up[1];
178226
//call
179-
JSONObject authInfo = JSON.make("userId", username,
180-
"pwd", password,
181-
"client", client_info);
182-
boolean success = authService.authenticate(authInfo);
183-
184-
//get basic info
185-
if (success){
186-
userId = authService.getUserID();
187-
accessLevel = authService.getAccessLevel();
188-
189-
info = authService.getBasicInfo();
190-
191-
//Language
192-
String lang_code = (String) info.get("user_lang_code");
193-
if (lang_code != null && !lang_code.isEmpty()){
194-
language = lang_code;
195-
}
196-
197-
//Name
198-
JSONObject user_n = (JSONObject) info.get("user_name");
199-
if (user_n != null && !user_n.isEmpty()){
200-
userName = user_n;
201-
userNameShort = getShortUserName();
202-
}
203-
204-
//Birth
205-
String user_birth = (String) info.get("user_birth");
206-
if (user_birth != null && !user_birth.isEmpty()){
207-
userBirth = user_birth;
208-
}
209-
210-
//Roles
211-
if (info.containsKey("user_roles")){
212-
userRoles = Converters.object2ArrayListStr(info.get("user_roles"));
213-
}else{
214-
userRoles = new ArrayList<>();
215-
}
227+
JSONObject authInfo = JSON.make(
228+
"userId", username,
229+
"pwd", password,
230+
"client", clientInfo
231+
);
232+
if (authService.authenticate(authInfo)){
233+
//get basic info
234+
copyBasicInfo();
235+
return true;
236+
}else{
237+
//fail
238+
return false;
216239
}
217-
return success;
218-
219240
//ERROR
220241
}else{
221242
log.error("AUTHENTICATION FAILED! Due to wrong KEY format: '" + key + "'");
222243
return false;
223244
}
224-
225245
//ERROR
226246
}else{
227247
log.error("AUTHENTICATION FAILED! Due to missing KEY");
228248
return false;
229249
}
230250
}
231251

252+
/**
253+
* Fill Account data with data received from successful authentication.
254+
*/
255+
public void copyBasicInfo(){
256+
userId = authService.getUserID();
257+
accessLevel = authService.getAccessLevel();
258+
259+
info = authService.getBasicInfo();
260+
261+
//Language
262+
String lang_code = (String) info.get("user_lang_code");
263+
if (lang_code != null && !lang_code.isEmpty()){
264+
language = lang_code;
265+
}
266+
267+
//Name
268+
JSONObject user_n = (JSONObject) info.get("user_name");
269+
if (user_n != null && !user_n.isEmpty()){
270+
userName = user_n;
271+
userNameShort = getShortUserName();
272+
}
273+
274+
//Birth
275+
String user_birth = (String) info.get("user_birth");
276+
if (user_birth != null && !user_birth.isEmpty()){
277+
userBirth = user_birth;
278+
}
279+
280+
//Roles
281+
if (info.containsKey("user_roles")){
282+
userRoles = Converters.object2ArrayListStr(info.get("user_roles"));
283+
}else{
284+
userRoles = new ArrayList<>();
285+
}
286+
}
287+
232288
/**
233289
* Export (part of) user account data to JSON string.
234290
*/

0 commit comments

Comments
 (0)