owntracks / frontend

🌍 Web interface for OwnTracks built with Vue.js
MIT License
359 stars 53 forks source link

Friend-Based Views #5

Open metal450 opened 5 years ago

metal450 commented 5 years ago

OwnTracks Recorder has the notion of "Friends" - users who are able to view each other's positions on the mobile app. It would be amazing if there were a way to extend this to the web UI - based on the current http-authenticated user, only allow to see that user's friends on the map.

metal450 commented 5 years ago

...Or, if not based on friends/auth, at least provide a way to hard-code a filter of which users can be displayed (then the web UI could be hosted in a auth-protected folder, coded to only show the desired users)

jpmens commented 5 years ago

A small correction if you'll permit: it's not the Recorder which has a notion of Friends, but our mobile Android/iOS apps; the Recorder doesn't care, unless it's running in HTTP mode in which case it is tasked with responding with a list of Friends and their positions.

I fear that if Friends support is added here, that would mean a whole user management thing which is a shame.

The idea with the filter on the other hand is good, IMO.

linusg commented 5 years ago

The whole auth thing is a mess, btw. Usually I visit my recorder instance first, which then forces me to authenticate. Firefox will remember the credentials until I quit the browser, and it will pass these to the HTTP API requests and the WebSocket request as well, since they're on the same subdomain as my UI. I'd prefer to have a login form in the UI, and pass credentials for basic auth to the requests manually - but the WebSocket API doesn't support custom headers :disappointed:

Long story short - I'm not even sure if the UI knows which user is authenticated, but sure, a filter can be added. It would replace fetching a list of available users and their devices and just trust that the filter is correct and each user / device exists, correct?

If I have three users foo, bar and baz, and they all have the devices phone and tablet, I'd do something like this:

visibleUsersAndDevices: {
    foo: ["phone", "tablet"],
    bar: ["phone"]
    // hide user 'baz' from dropdown
}

alternatively this:

visibleUsersAndDevices: {
    foo: true,
    bar: ["phone"]
}
metal450 commented 5 years ago

It would replace fetching a list of available users and their devices and just trust that the filter is correct and each user / device exists, correct?

Well, more robustly, what I had in mind was that it still pulls the list of users as it does now, but you can specify the list of users to filter those by. I don't think you need to filter by device too, that seems unnecessary granularity - if you can see a given user, might as well just see all of their devices.

So something like:

visibleUsers: ["foo1", "foo2"]

If the recorder has users foo1, foo2, foo3, it will show foo1 and foo2. If the recorder has bar1, bar2, bar3, it won't show any. *If the recorder has foo1, bar1, it will show foo1 ...and so forth.

Note: shame there's no way to tell the logged-in user - if that ends up not being the case, I had originally envisioned something more like:

{ foo1:["bar1", "baz2"] , //If logged in as foo1, you can only see users bar1 & baz2 foo2:["bar1"] //If logged in as foo2, you can only see bar1 }

That way you can just make one central "permissions list" to determine what everyone can and can't see :)

linusg commented 5 years ago

Got it, thanks for the clarification!

metal450 commented 5 years ago

Ok, so I actually ended up putting a bit of time into this myself, & wanted to share my thoughts.

As far as making the filter itself, super easy:

In config/custom.js, define an array allowedUsers: ['user1', 'user2']

Then in main.js getUsers(): return users.filter( user=>config.allowedUsers.includes(user) || config.allowedUsers.includes("*") ); (asterisk to support wildcard - i.e. 'don't filter')

And likewise in getLastLocations(): return json.filter(user=>config.allowedUsers.includes(user.username) || config.allowedUsers.includes("*"));

Done - now the map will only show users specified in the list.

Next I got to thinking about how I could create maps for different users (aka per-user "allowed lists"). Obviously extending allowedUsers from a single array to a set of userName=>accessList is trivial, so I instead started with how to handle authentication.

My plan was to access the UI with query params, like http://example.com/ui/?user=user1. Authentication is handled by the reverse proxy, so it'll only allow user1 to actually get to the url where user=user1. However, there's a problem: if we're HTTP-authenticated as User1...we can't call into the Recorder's API unless that user also has full, unrestricted access to the API (since the API calls are being performed by client-side javascript). I can't really think of a way around this. So although it seems like it works for making per-user maps...it also requires unsecuring the backend, so any user with basic knowledge of how it works can still ultimately access whatever information they want.

I can't seem to figure out any solution...

nma83 commented 5 years ago

A small correction if you'll permit: it's not the Recorder which has a notion of Friends, but our mobile Android/iOS apps; the Recorder doesn't care, unless it's running in HTTP mode in which case it is tasked with responding with a list of Friends and their positions.

I fear that if Friends support is added here, that would mean a whole user management thing which is a shame.

The idea with the filter on the other hand is good, IMO.

It is interesting that @jpmens refers to user management here. I was thinking of creating a standard user management framework in conjunction with mosquitto-auth-lib to provide a generic location sharing framework. This would be a web app managing the users and ACLs for the mosquitto broker that Owntracks app would use.