Skip to content

Commit 1e362ed

Browse files
committed
ANDROID: implemented IP based authentication for the web portal
1 parent fc659cf commit 1e362ed

File tree

4 files changed

+71
-26
lines changed

4 files changed

+71
-26
lines changed

src/platform/android/app/src/main/java/net/sourceforge/smallbasic/MainActivity.java

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ public class MainActivity extends NativeActivity {
103103
private final ExecutorService _audioExecutor = Executors.newSingleThreadExecutor();
104104
private final Queue<Sound> _sounds = new ConcurrentLinkedQueue<>();
105105
private final Handler _keypadHandler = new Handler(Looper.getMainLooper());
106+
private final Map<String, Boolean> permittedHost = new HashMap<>();
106107
private String[] _options = null;
107108
private MediaPlayer _mediaPlayer = null;
108109
private LocationAdapter _locationAdapter = null;
@@ -578,7 +579,7 @@ public void showAlert(final byte[] titleBytes, final byte[] messageBytes) {
578579
public void run() {
579580
new AlertDialog.Builder(activity)
580581
.setTitle(title).setMessage(message)
581-
.setPositiveButton("OK", new DialogInterface.OnClickListener() {
582+
.setPositiveButton(R.string.OK, new DialogInterface.OnClickListener() {
582583
public void onClick(DialogInterface dialog, int which) {}
583584
}).show();
584585
}
@@ -771,6 +772,10 @@ private void installSamples() {
771772
}
772773
}
773774

775+
private boolean isHostPermitted(String remoteHost) {
776+
return (remoteHost != null && permittedHost.get(remoteHost) != null && Boolean.TRUE.equals(permittedHost.get(remoteHost)));
777+
}
778+
774779
private boolean locationPermitted() {
775780
String permission = Manifest.permission.ACCESS_FINE_LOCATION;
776781
return (ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED);
@@ -848,6 +853,26 @@ private String readLine(InputStream inputReader) throws IOException {
848853
return b == -1 ? null : out.size() == 0 ? "" : out.toString();
849854
}
850855

856+
private void requestHostPermission(String remoteHost) {
857+
final Activity activity = this;
858+
runOnUiThread(new Runnable() {
859+
public void run() {
860+
new AlertDialog.Builder(activity)
861+
.setTitle(R.string.PORTAL_PROMPT).setMessage(getString(R.string.PORTAL_QUESTION, remoteHost))
862+
.setPositiveButton(R.string.OK, new DialogInterface.OnClickListener() {
863+
public void onClick(DialogInterface dialog, int which) {
864+
permittedHost.put(remoteHost, Boolean.TRUE);
865+
}
866+
})
867+
.setNegativeButton(R.string.Cancel, new DialogInterface.OnClickListener() {
868+
public void onClick(DialogInterface dialog, int which) {
869+
permittedHost.put(remoteHost, Boolean.FALSE);
870+
}
871+
}).show();
872+
}
873+
});
874+
}
875+
851876
private String saveSchemeData(final String buffer) throws IOException {
852877
File outputFile = new File(_storage.getInternal(), SCHEME_BAS);
853878
BufferedWriter output = new BufferedWriter(new FileWriter(outputFile));
@@ -863,6 +888,12 @@ private void setupStorageEnvironment() {
863888
setenv("LEGACY_DIR", _storage.getMedia());
864889
}
865890

891+
private void validateAccess(String remoteHost) throws IOException {
892+
if (!isHostPermitted(remoteHost)) {
893+
throw new IOException("Access denied");
894+
}
895+
}
896+
866897
private static class BasFileFilter implements FilenameFilter {
867898
@Override
868899
public boolean accept(File dir, String name) {
@@ -944,7 +975,8 @@ protected byte[] decodeBase64(String data) {
944975
}
945976

946977
@Override
947-
protected void deleteFile(String fileName) throws IOException {
978+
protected void deleteFile(String remoteHost, String fileName) throws IOException {
979+
validateAccess(remoteHost);
948980
if (fileName == null) {
949981
throw new IOException("Empty file name");
950982
}
@@ -963,14 +995,18 @@ protected void execStream(InputStream inputStream) throws IOException {
963995
}
964996

965997
@Override
966-
protected Response getFile(String path, boolean asset) throws IOException {
998+
protected Response getFile(String remoteHost, String path, boolean asset) throws IOException {
967999
Response result;
9681000
if (asset) {
9691001
String name = "webui/" + path;
9701002
long length = getFileLength(name);
9711003
log("Opened " + name + " " + length + " bytes");
9721004
result = new Response(getAssets().open(name), length);
1005+
if ("index.html".equals(path) && !isHostPermitted(remoteHost)) {
1006+
requestHostPermission(remoteHost);
1007+
}
9731008
} else {
1009+
validateAccess(remoteHost);
9741010
File file = getFile(path);
9751011
if (file != null) {
9761012
result = new Response(new FileInputStream(file), file.length());
@@ -982,7 +1018,8 @@ protected Response getFile(String path, boolean asset) throws IOException {
9821018
}
9831019

9841020
@Override
985-
protected Collection<FileData> getFileData() throws IOException {
1021+
protected Collection<FileData> getFileData(String remoteHost) throws IOException {
1022+
validateAccess(remoteHost);
9861023
Collection<FileData> result = new ArrayList<>();
9871024
result.addAll(getFiles(new File(_storage.getExternal())));
9881025
result.addAll(getFiles(new File(_storage.getMedia())));
@@ -1001,7 +1038,8 @@ protected void log(String message) {
10011038
}
10021039

10031040
@Override
1004-
protected void renameFile(String from, String to) throws IOException {
1041+
protected void renameFile(String remoteHost, String from, String to) throws IOException {
1042+
validateAccess(remoteHost);
10051043
if (to == null) {
10061044
throw new IOException("Empty file name");
10071045
}
@@ -1019,7 +1057,8 @@ protected void renameFile(String from, String to) throws IOException {
10191057
}
10201058

10211059
@Override
1022-
protected void saveFile(String fileName, byte[] content) throws IOException {
1060+
protected void saveFile(String remoteHost, String fileName, byte[] content) throws IOException {
1061+
validateAccess(remoteHost);
10231062
File file = new File(_storage.getExternal(), fileName);
10241063
if (file.exists()) {
10251064
throw new IOException("File already exists: " + fileName);

src/platform/android/app/src/main/java/net/sourceforge/smallbasic/WebServer.java

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import java.io.InputStream;
99
import java.io.OutputStream;
1010
import java.io.UnsupportedEncodingException;
11+
import java.net.InetSocketAddress;
1112
import java.net.ServerSocket;
1213
import java.net.Socket;
1314
import java.net.URLDecoder;
@@ -32,7 +33,7 @@
3233
* @author chrisws
3334
*/
3435
public abstract class WebServer {
35-
private static final int BUFFER_SIZE = 32768 / 2;
36+
private static final int BUFFER_SIZE = 32768;
3637
private static final int SEND_SIZE = BUFFER_SIZE / 4;
3738
private static final int LINE_SIZE = 128;
3839
private static final String UTF_8 = "utf-8";
@@ -62,15 +63,15 @@ public void run() {
6263
socketThread.start();
6364
}
6465

65-
protected abstract void deleteFile(String fileName) throws IOException;
66+
protected abstract void deleteFile(String remoteHost, String fileName) throws IOException;
6667
protected abstract void execStream(InputStream inputStream) throws IOException;
67-
protected abstract Response getFile(String path, boolean asset) throws IOException;
68-
protected abstract Collection<FileData> getFileData() throws IOException;
68+
protected abstract Response getFile(String remoteHost, String path, boolean asset) throws IOException;
69+
protected abstract Collection<FileData> getFileData(String remoteHost) throws IOException;
6970
protected abstract byte[] decodeBase64(String data);
7071
protected abstract void log(String message);
7172
protected abstract void log(String message, Exception exception);
72-
protected abstract void renameFile(String from, String to) throws IOException;
73-
protected abstract void saveFile(String fileName, byte[] content) throws IOException;
73+
protected abstract void renameFile(String remoteHost, String from, String to) throws IOException;
74+
protected abstract void saveFile(String remoteHost, String fileName, byte[] content) throws IOException;
7475

7576
/**
7677
* WebServer main loop to be run in a separate thread
@@ -108,13 +109,15 @@ public abstract class AbstractRequest {
108109
final String tokenKey;
109110
final List<String> headers;
110111
final InputStream inputStream;
112+
final String remoteHost;
111113

112114
public AbstractRequest(Socket socket, String tokenKey) throws IOException {
113115
this.socket = socket;
114116
this.tokenKey = tokenKey;
115117
this.inputStream = socket.getInputStream();
116118
this.headers = getHeaders();
117119
this.requestToken = getToken(headers);
120+
this.remoteHost = ((InetSocketAddress) socket.getRemoteSocketAddress()).getHostName();
118121
String first = headers.size() > 0 ? headers.get(0) : null;
119122
String[] fields;
120123
if (first != null) {
@@ -387,7 +390,7 @@ private void afterRun() {
387390
*/
388391
private Collection<String> getAllFileNames() throws IOException {
389392
Collection<String> result = new ArrayList<>();
390-
for (FileData fileData : getFileData()) {
393+
for (FileData fileData : getFileData(remoteHost)) {
391394
result.add(fileData.fileName);
392395
}
393396
return result;
@@ -416,13 +419,13 @@ private Response handleDownload(Map<String, Collection<String>> data) throws IOE
416419
result = handleStatus(false, "File list is empty");
417420
} else if (fileNames.size() == 1) {
418421
// plain text download single file
419-
result = getFile(fileNames.iterator().next(), false);
422+
result = getFile(remoteHost, fileNames.iterator().next(), false);
420423
} else {
421424
// download multiple as zip
422425
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
423426
ZipOutputStream zipOutputStream = new ZipOutputStream(outputStream);
424427
for (String fileName : fileNames) {
425-
Response response = getFile(fileName, false);
428+
Response response = getFile(remoteHost, fileName, false);
426429
ZipEntry entry = new ZipEntry(fileName);
427430
zipOutputStream.putNextEntry(entry);
428431
response.toStream(zipOutputStream);
@@ -444,7 +447,7 @@ private Response handleFileList() throws IOException {
444447
builder.append('[');
445448
long id = 0;
446449
char comma = 0;
447-
for (FileData nextFile : getFileData()) {
450+
for (FileData nextFile : getFileData(remoteHost)) {
448451
builder.append(comma);
449452
builder.append('{');
450453
builder.append("id", id++, true);
@@ -516,7 +519,7 @@ private Response handleDelete(Map<String, FormField> data) throws IOException {
516519
String fileName = getString(data, "fileName");
517520
Response result;
518521
try {
519-
deleteFile(fileName);
522+
deleteFile(remoteHost, fileName);
520523
log("Deleted " + fileName);
521524
result = handleFileList();
522525
} catch (IOException e) {
@@ -533,7 +536,7 @@ private Response handleRename(Map<String, FormField> data) throws IOException {
533536
String to = getString(data, "to");
534537
Response result;
535538
try {
536-
renameFile(from, to);
539+
renameFile(remoteHost, from, to);
537540
result = handleStatus(true, "File renamed");
538541
} catch (IOException e) {
539542
result = handleStatus(false, e.getMessage());
@@ -575,7 +578,7 @@ private Response handleUpload(Map<String, FormField> data) throws IOException {
575578
if (fileName == null || content == null) {
576579
result = handleStatus(false, "Invalid input");
577580
} else {
578-
saveFile(fileName, content);
581+
saveFile(remoteHost, fileName, content);
579582
result = handleStatus(true, "File saved");
580583
}
581584
} catch (Exception e) {
@@ -598,7 +601,7 @@ private Response handleWebResponse(String asset) throws IOException {
598601
}
599602
Response result;
600603
try {
601-
result = getFile(path, true);
604+
result = getFile(remoteHost, path, true);
602605
} catch (Exception e) {
603606
log("Error: " + e);
604607
result = null;

src/platform/android/app/src/main/res/values/strings.xml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<resources>
33
<string name="app_name">SmallBASIC</string>
4-
<string name="samsung_keyboard">Samsung keyboard not supported. Please use an alternative keyboard.</string>
4+
<string name="PORTAL_PROMPT">Allow Web portal access?</string>
5+
<string name="PORTAL_QUESTION">Do you wish to allow anyone with IP address [%s] to gain access to the portal?</string>
6+
<string name="OK">OK</string>
7+
<string name="Cancel">Cancel</string>
58
<style name="SBTheme" parent="android:Theme.Holo.Light">
69
<item name="android:windowActionBar">false</item>
710
<item name="android:windowNoTitle">true</item>

src/platform/android/webui/server/src/main/java/net/sourceforge/smallbasic/Server.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ protected byte[] decodeBase64(String data) {
3232
}
3333

3434
@Override
35-
protected void deleteFile(String fileName) throws IOException {
35+
protected void deleteFile(String hostName, String fileName) throws IOException {
3636
Files.delete(Paths.get(BASIC_HOME, fileName));
3737
}
3838

@@ -48,14 +48,14 @@ protected void execStream(InputStream inputStream) {
4848
}
4949

5050
@Override
51-
protected Response getFile(String path, boolean asset) throws IOException {
51+
protected Response getFile(String hostName, String path, boolean asset) throws IOException {
5252
String prefix = asset ? "../build/" : BASIC_HOME;
5353
File file = new File(prefix + path);
5454
return new Response(Files.newInputStream(file.toPath()), file.length());
5555
}
5656

5757
@Override
58-
protected Collection<FileData> getFileData() throws IOException {
58+
protected Collection<FileData> getFileData(String hostName) throws IOException {
5959
final File folder = new File(BASIC_HOME);
6060
Collection<FileData> result = new ArrayList<>();
6161
for (final File fileEntry : Objects.requireNonNull(folder.listFiles())) {
@@ -84,7 +84,7 @@ protected void log(String message, Exception exception) {
8484
}
8585

8686
@Override
87-
protected void renameFile(String from, String to) throws IOException {
87+
protected void renameFile(String hostName, String from, String to) throws IOException {
8888
if (to == null) {
8989
throw new IOException("Invalid File Name: " + to);
9090
}
@@ -102,7 +102,7 @@ protected void renameFile(String from, String to) throws IOException {
102102
}
103103

104104
@Override
105-
protected void saveFile(String fileName, byte[] content) throws IOException {
105+
protected void saveFile(String hostName, String fileName, byte[] content) throws IOException {
106106
File file = new File(BASIC_HOME, fileName);
107107
if (file.exists()) {
108108
throw new IOException("File already exists: " + fileName);

0 commit comments

Comments
 (0)