mirror of
https://github.com/falk-werner/webfuse
synced 2024-10-27 20:34:10 +00:00
add in-protocol authentication mechanism
This commit is contained in:
parent
9423d75021
commit
5db3b28b5a
@ -1,12 +1,21 @@
|
||||
# Authentication
|
||||
|
||||
Webfuse supports token-based authentication using HTTP headers. To activate authentication, two command line option needs to be given:
|
||||
Webfuse supports two authentications mechanisms:
|
||||
|
||||
- token-based authentication using HTTP headers
|
||||
- in-protocol authentication
|
||||
|
||||
To activate authentication, two command line option needs to be given:
|
||||
|
||||
- `--wf-authenticator PATH`
|
||||
allows to specify an executable used for authentication
|
||||
- `--wf-auth-header HEADER`
|
||||
- `--wf-auth-header HEADER` _(optional)_
|
||||
allows to specify the HTTP header used for authentication
|
||||
|
||||
When `--wf-auth-header` is not specifiend or the header is not contained
|
||||
in the HTTP request, the in-protocol solutions is used: Before any other
|
||||
operation, the credentials are queried via `getcreds`request.
|
||||
|
||||
## Authenticator
|
||||
|
||||
An authenticator is an executable or script used for token-based
|
||||
|
@ -313,6 +313,7 @@ _Note that the following numbers are in `hexadecimal` notation._
|
||||
| rmdir | 0x14 | 0x94 |
|
||||
| statfs | 0x15 | 0x95 |
|
||||
| utimens | 0x16 | 0x96 |
|
||||
| getcreds | 0x17 | 0x97 |
|
||||
|
||||
## Methods
|
||||
|
||||
@ -807,6 +808,29 @@ _Note that handle might be invalid (-1), even if the file is open._
|
||||
| type | u8 | message type (0x96) |
|
||||
| result | result | operation status |
|
||||
|
||||
### getcreds
|
||||
|
||||
Query credentials. When authentication is active and the in-protocol
|
||||
authentication mechanism is used, this is the first request a
|
||||
webfuse service sends to a provider.
|
||||
|
||||
#### Request
|
||||
|
||||
| Field | Data Type | Description |
|
||||
| ------ | --------- | ----------- |
|
||||
| id | u32 | message id |
|
||||
| type | u8 | message type (0x17) |
|
||||
|
||||
_Note that handle might be invalid (-1), even if the file is open._
|
||||
|
||||
#### Response
|
||||
|
||||
| Field | Data Type | Description |
|
||||
| ------ | --------- | ----------- |
|
||||
| id | u32 | message id |
|
||||
| type | u8 | message type (0x97) |
|
||||
| creds | str | credentials |
|
||||
|
||||
## Examples
|
||||
|
||||
### Get file attributes
|
||||
|
@ -5,6 +5,7 @@ import os
|
||||
import stat
|
||||
import websockets
|
||||
import errno
|
||||
import getpass
|
||||
|
||||
INVALID_FD = 0xffffffffffffffff
|
||||
|
||||
@ -269,10 +270,12 @@ class FilesystemProvider:
|
||||
0x14: FilesystemProvider.rmdir,
|
||||
0x15: FilesystemProvider.statfs,
|
||||
0x16: FilesystemProvider.utimens,
|
||||
0x17: FilesystemProvider.getcreds,
|
||||
}
|
||||
|
||||
async def run(self):
|
||||
async with websockets.connect(self.url, extra_headers=[("X-Auth-Token", "user:bob;token=foo")]) as connection:
|
||||
extra_headers = [("X-Auth-Token", "user:bob;token=foo")]
|
||||
async with websockets.connect(self.url, extra_headers=extra_headers) as connection:
|
||||
while True:
|
||||
request = await connection.recv()
|
||||
reader = MessageReader(request)
|
||||
@ -565,7 +568,11 @@ class FilesystemProvider:
|
||||
writer.write_u64(buffer.f_files)
|
||||
writer.write_u64(buffer.f_ffree)
|
||||
writer.write_u64(buffer.f_namemax)
|
||||
|
||||
|
||||
def getcreds(self, reader, writer):
|
||||
credentials = getpass.getpass(prompt="credentials: ")
|
||||
writer.write_str(credentials)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
provider = FilesystemProvider('.', 'ws://localhost:8081')
|
||||
|
@ -418,6 +418,11 @@ public:
|
||||
return (result == 0) ? 0 : -errno;
|
||||
}
|
||||
|
||||
std::string get_credentials() override
|
||||
{
|
||||
return getpass("credentials: ");
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
std::string get_full_path(std::string const & path)
|
||||
|
@ -419,4 +419,11 @@ int filesystem::statfs(std::string const & path, struct statvfs * statistics)
|
||||
}
|
||||
}
|
||||
|
||||
// get credentials is handled internally
|
||||
std::string filesystem::get_credentials()
|
||||
{
|
||||
throw std::runtime_error("not implemented");
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -47,6 +47,7 @@ public:
|
||||
|
||||
int statfs(std::string const & path, struct statvfs * statistics) override;
|
||||
|
||||
std::string get_credentials() override;
|
||||
|
||||
private:
|
||||
ws_server &proxy;
|
||||
|
@ -131,5 +131,11 @@ int empty_filesystem::statfs(std::string const & path, struct statvfs * statisti
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
std::string empty_filesystem::get_credentials()
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
@ -39,6 +39,8 @@ public:
|
||||
int rmdir(std::string const & path) override;
|
||||
|
||||
int statfs(std::string const & path, struct statvfs * statistics) override;
|
||||
|
||||
std::string get_credentials() override;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -48,6 +48,8 @@ public:
|
||||
virtual int rmdir(std::string const & path) = 0;
|
||||
|
||||
virtual int statfs(std::string const & path, struct statvfs * statistics) = 0;
|
||||
|
||||
virtual std::string get_credentials() = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -122,6 +122,9 @@ public:
|
||||
case request_type::statfs:
|
||||
fs_statfs(reader, writer);
|
||||
break;
|
||||
case request_type::getcreds:
|
||||
fs_get_credentials(reader, writer);
|
||||
break;
|
||||
default:
|
||||
std::cout << "unknown request: " << ((int) req_type) << std::endl;
|
||||
break;
|
||||
@ -371,6 +374,13 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
void fs_get_credentials(messagereader & reader, messagewriter & writer)
|
||||
{
|
||||
std::string credentials = fs_.get_credentials();
|
||||
writer.write_str(credentials);
|
||||
}
|
||||
|
||||
|
||||
filesystem_i & fs_;
|
||||
ws_client client;
|
||||
};
|
||||
|
@ -29,6 +29,7 @@ request_type get_request_type(uint8_t value)
|
||||
case static_cast<uint8_t>(request_type::rmdir): return request_type::rmdir;
|
||||
case static_cast<uint8_t>(request_type::statfs): return request_type::statfs;
|
||||
case static_cast<uint8_t>(request_type::utimens): return request_type::utimens;
|
||||
case static_cast<uint8_t>(request_type::getcreds): return request_type::getcreds;
|
||||
default:
|
||||
return request_type::unknown;
|
||||
}
|
||||
|
@ -30,7 +30,8 @@ enum class request_type: uint8_t
|
||||
readdir = 0x13,
|
||||
rmdir = 0x14,
|
||||
statfs = 0x15,
|
||||
utimens = 0x16
|
||||
utimens = 0x16,
|
||||
getcreds =0x17
|
||||
};
|
||||
|
||||
request_type get_request_type(uint8_t value);
|
||||
|
@ -29,6 +29,7 @@ response_type get_response_type(request_type value)
|
||||
case request_type::rmdir: return response_type::rmdir;
|
||||
case request_type::statfs: return response_type::statfs;
|
||||
case request_type::utimens: return response_type::utimens;
|
||||
case request_type::getcreds: return response_type::getcreds;
|
||||
default:
|
||||
return response_type::unknown;
|
||||
}
|
||||
|
@ -31,7 +31,8 @@ enum class response_type: uint8_t
|
||||
readdir = 0x93,
|
||||
rmdir = 0x94,
|
||||
statfs = 0x95,
|
||||
utimens = 0x96
|
||||
utimens = 0x96,
|
||||
getcreds = 0x97
|
||||
};
|
||||
|
||||
response_type get_response_type(request_type value);
|
||||
|
@ -54,7 +54,7 @@ static int ws_server_callback(struct lws *wsi, enum lws_callback_reasons reason,
|
||||
handler->on_receive(wsi, in, len);
|
||||
break;
|
||||
case LWS_CALLBACK_SERVER_WRITEABLE:
|
||||
handler->on_writable();
|
||||
result = handler->on_writable();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -51,6 +51,7 @@ namespace webfuse
|
||||
server_handler::server_handler(std::string const & auth_app, std::string const & auth_hdr)
|
||||
: connection(nullptr)
|
||||
, id(0)
|
||||
, shutdown_requested(false)
|
||||
, is_authenticated(false)
|
||||
, authenticator(auth_app)
|
||||
, auth_header(auth_hdr)
|
||||
@ -69,9 +70,26 @@ int server_handler::on_established(lws * wsi)
|
||||
if (nullptr == connection)
|
||||
{
|
||||
connection = wsi;
|
||||
id = 0;
|
||||
|
||||
if ((!is_authenticated) && (!authenticator.empty()))
|
||||
{
|
||||
{
|
||||
messagewriter writer(request_type::getcreds);
|
||||
std::lock_guard<std::mutex> lock(mut);
|
||||
|
||||
uint32_t id = next_id();
|
||||
writer.set_id(id);
|
||||
requests.emplace(std::move(writer));
|
||||
}
|
||||
|
||||
lws_callback_on_writable(wsi);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// already connected: refuse
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -86,22 +104,29 @@ void server_handler::on_receive(lws * wsi, void* in, int len)
|
||||
{
|
||||
webfuse::messagereader reader(current_message);
|
||||
uint32_t id = reader.read_u32();
|
||||
reader.read_u8(); // read message type: ToDo: use it
|
||||
auto const message_type = reader.read_u8(); // read message type: ToDo: use it
|
||||
|
||||
std::lock_guard<std::mutex> lock(mut);
|
||||
auto it = pending_responses.find(id);
|
||||
if (it != pending_responses.end())
|
||||
if (static_cast<response_type>(message_type) == response_type::getcreds)
|
||||
{
|
||||
it->second.set_value(std::move(reader));
|
||||
pending_responses.erase(it);
|
||||
finish_authentication(wsi, std::move(reader));
|
||||
}
|
||||
else
|
||||
{
|
||||
// ToDo: log request not found
|
||||
std::cout << "warning: request not found: id=" << id << std::endl;
|
||||
for(auto const & entry: pending_responses)
|
||||
std::lock_guard<std::mutex> lock(mut);
|
||||
auto it = pending_responses.find(id);
|
||||
if (it != pending_responses.end())
|
||||
{
|
||||
std::cout << "\t" << entry.first << std::endl;
|
||||
it->second.set_value(std::move(reader));
|
||||
pending_responses.erase(it);
|
||||
}
|
||||
else
|
||||
{
|
||||
// ToDo: log request not found
|
||||
std::cout << "warning: request not found: id=" << id << std::endl;
|
||||
for(auto const & entry: pending_responses)
|
||||
{
|
||||
std::cout << "\t" << entry.first << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -113,8 +138,10 @@ void server_handler::on_receive(lws * wsi, void* in, int len)
|
||||
}
|
||||
}
|
||||
|
||||
void server_handler::on_writable()
|
||||
int server_handler::on_writable()
|
||||
{
|
||||
if (shutdown_requested) { return -1; }
|
||||
|
||||
webfuse::messagewriter writer(webfuse::request_type::unknown);
|
||||
bool has_msg = false;
|
||||
bool has_more = false;
|
||||
@ -143,6 +170,8 @@ void server_handler::on_writable()
|
||||
{
|
||||
lws_callback_on_writable(connection);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void server_handler::on_closed(lws * wsi)
|
||||
@ -258,5 +287,22 @@ uint32_t server_handler::next_id()
|
||||
return id;
|
||||
}
|
||||
|
||||
void server_handler::finish_authentication(lws * wsi, messagereader reader)
|
||||
{
|
||||
auto const credentials = reader.read_str();
|
||||
|
||||
webfuse::authenticator auth(authenticator);
|
||||
auto const result = auth.authenticate(credentials);
|
||||
if (result)
|
||||
{
|
||||
is_authenticated = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
shutdown_requested = true;
|
||||
lws_callback_on_writable(wsi);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -25,7 +25,7 @@ public:
|
||||
|
||||
int on_established(lws* wsi);
|
||||
void on_receive(lws * wsi, void* in, int len);
|
||||
void on_writable();
|
||||
int on_writable();
|
||||
void on_closed(lws * wsi);
|
||||
|
||||
std::future<messagereader> perform(messagewriter writer);
|
||||
@ -35,9 +35,11 @@ private:
|
||||
int authenticate_via_header(lws * wsi);
|
||||
std::string get_auth_token(lws * wsi) const;
|
||||
uint32_t next_id();
|
||||
void finish_authentication(lws * wsi, messagereader reader);
|
||||
|
||||
struct lws * connection;
|
||||
uint32_t id;
|
||||
bool shutdown_requested;
|
||||
|
||||
std::atomic<bool> is_authenticated;
|
||||
std::string authenticator;
|
||||
|
@ -40,6 +40,9 @@ public:
|
||||
MOCK_METHOD(int, rmdir, (std::string const & path));
|
||||
|
||||
MOCK_METHOD(int, statfs, (std::string const & path, struct statvfs * statistics));
|
||||
|
||||
MOCK_METHOD(std::string, get_credentials, ());
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user