Coder Social home page Coder Social logo

Comments (16)

jameswestnz avatar jameswestnz commented on August 16, 2024 1

Looking good! Cool to see this moving, as I've had to hack this together in our app :/

Just an initial thought: Could we merge the following methods?

Store.grantAccess()
Store.grantWriteAccess()

Something like:

Store.grantAccess('mydb', ['username1', 'username2', ...], {
  read: true,
  write: false
})

This means we could have other rights in the future - like security for adding users to a db, etc. We may also be able to shorten the method grantAccess to simply grant, as the third parameter is defining the "access".

Also, while a Store doesn't exist, this makes sense: Store.create('mydb'), however it feels that once you're dealing with an existing store, the syntax should be something like Store('mydb').grant(...)

Not sure if this helps?

from discussion.

gr2m avatar gr2m commented on August 16, 2024 1

I’ve replaced

store = new Store(name)

with

Store.open(name).then(function (store) {})

That way we have an async method to get a store instance in which we can prevent PouchDB from creating a database if it does not exist, so we can make sure that no database gets created without security

from discussion.

gr2m avatar gr2m commented on August 16, 2024 1

I’ve updated the README with the API, it’s work in progress:
https://github.com/hoodiehq/hoodie-store-server/tree/discussion/100/store-server-api/api

As I worked on it and made API docs and examples for every method, I’ve lost myself in a requirement we got a few times in a past to be able to set password protection for a store. What I ended up with is the idea of different "access types". By default the access type is "role", so you can grant read / write access to roles like this:

Store.grant('foo', {
  role: 'acme-inc', // array sets multiple roles
  access: 'read'
})

or give public read access like this

Store.grant('foo', {
  access: 'read'
})

In order to check if a user with the role acme-inc has access, you can do

Store.hasAccess('foo', {
  role: 'acme-inc', // array picks any role
  access: 'read'
})

That is the default behavior that Hoodie will provide, no more. The benefit is that it can be mirrored with CouchDB’s _security settings and custom design docs (with the exception of write only)

In future, we could add APIs where plugins can register custom access types, like 'password', so that a call like this would be handled by the plugin

Store.grant('foo', {
  type: 'password',
  access: 'read',
  role: 'acme-inc',
  password: 'secret'
})

I also ended up calling the methods Store.grant, Store.revoke and Store.hasAccess, even if access repeats itself in the options for the latter, I think it’s better than just Store.has and I didn’t come up with a better name yet.

from discussion.

gr2m avatar gr2m commented on August 16, 2024 1

My thinking is that the question gets answered correctly with a yes or no, either way the method behaved as expected. It would only reject if something unexpected would go wrong, like a connection error. In that regard I’d say it’s different than e.g. hoodie.store.find(id) because it reads like I expect a document with id to exist, and if it doesn't it’s an error

from discussion.

jameswestnz avatar jameswestnz commented on August 16, 2024

As mentioned on slack (edited):

I’m been keen to understand a bit more about how the PouchDB option could work. (mentioned by @janl on slack)

In the absence of what is outlined above, we have built(hacked might be a better word) something similar as we had a need to create DBs for different entities.

Our structure is something like this:
Entities have admins and employees. Admins can create and assign jobs to employees. An employee can be “upgraded” to an admin.

So our Hoodie/CouchDB structure becomes:
When a entity is created, I add this new entity to a “entities” db, then create two new dbs specific to that entity - entity/12345 and entity/12345/admin. The latter because there is certain info I don’t want employees having access to, and I couldn’t be bothered with filtered replication for this task (possibly the wrong move, not sure yet). I then simply add a users’ role to the entities db based on their role in the entity. So if a user is an “employee”, I only add their id to the entity/12345 db. Now when an admin adds a job to this db it gets synced to the user, etc. So what is outlined above in theory helps with a large portion of our process.

There are a number of other solutions I considered, too. One option was to replicate individual docs from the entity db to the users personal db. This solution would be more efficient longer term, but has a slightly more complicated setup. And I think that regardless of these two options, I wanted to have a entity/12345 db so that all information for a given entity was stored in a common place.

Maybe this situation helps expand on the concept a little?

CC @gr2m @janl @boennemann

from discussion.

jameswestnz avatar jameswestnz commented on August 16, 2024

It's worth pointing out that if I went with my other option I mentioned above (per-doc replication to a users' db) It would(may) remove the need to grant/revoke access to a db, as the data would be filtered and synced based on some form of server-side logic.

So if I went down this path, I'd need some logic in the background to make it easy to assign a type of data (most likely a filter function) to a users' db.

I also avoided this example as we may in the future allow a user to belong to multiple entities... meaning I didn't want to deal with changing a users' db structure etc. But that's me getting too deep into our business needs, not hoodie's needs ;)

from discussion.

jameswestnz avatar jameswestnz commented on August 16, 2024

@gr2m think that's a nicer way to separate the factory from the constructor/use of the store - think it was slightly ambiguous before on how to differentiate one store from another etc.

from discussion.

gr2m avatar gr2m commented on August 16, 2024

If we have Store.grant() and Store.revoke() methods to grant / revoke access to / from a user, what should we call the method with which we can check if a user has the required access for an operation?

Maybe Store.has(), or in that case Store.hasAccess()? E.g.

Store.hasAccess(dbName, {read: 'id:123'})

from discussion.

jameswestnz avatar jameswestnz commented on August 16, 2024

Hmmm good question. '.has' is consistent, but ambiguous in this context... you could use '.can' (I.e. 'Store.can({read:[]})'), but doesn't exactly resolve the ambiguity.

Maybe my idea of removing the word access has proven to not work now?

from discussion.

gr2m avatar gr2m commented on August 16, 2024

Maybe my idea of removing the word access has proven to not work now?

Yeah feels like it right now. But this is a process, we’ll come up with something good at the end. I’ll move forward with grantAccess / revokeAccess / hasAccess for now and see how it feels

from discussion.

jameswestnz avatar jameswestnz commented on August 16, 2024

Totally, sounds good to me!

from discussion.

gr2m avatar gr2m commented on August 16, 2024

The current solution creates a meta database to keep track of all databases, as this is not something that PouchDB provides by default. There is https://github.com/nolanlawson/pouchdb-all-dbs which does the same but in more complicated way, as it most be compatible with the synchronous new PouchDB(name) while we could agree on having an asynchronous Store.open method, which can be async and can do all kind of checks before returning a store API instance.

In my current implementation I also store security in that database, which has the benefit that we can query what databases a user / a role has access to in future. If the backend is CouchDB, we can additionally keep CouchDB’s /_security in sync for double security

from discussion.

gr2m avatar gr2m commented on August 16, 2024

I implemented all but Store.replication API and the events, let me know what you think 💭

from discussion.

jameswestnz avatar jameswestnz commented on August 16, 2024

I see that store.exists resolves with a boolean value stating if the store exists or not. I'm not sure on the answer, but should the promise not resolve if it does exist, and reject if the store doesn't exist? I suppose the user needs to know if there's another reason for rejection, as they may be trying to create if it doesn't exist... Just thought I'd ask?

from discussion.

gr2m avatar gr2m commented on August 16, 2024

the PR is ready for review: hoodiehq/hoodie-store-server#45

from discussion.

gr2m avatar gr2m commented on August 16, 2024

we have https://github.com/hoodiehq/hoodie-store-server-api now :)

from discussion.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.