Skip to content
This repository has been archived by the owner on Oct 15, 2024. It is now read-only.

REST API: sharing of configuration snippets #921

Merged
merged 54 commits into from
Nov 29, 2016
Merged

REST API: sharing of configuration snippets #921

merged 54 commits into from
Nov 29, 2016

Conversation

Namoshek
Copy link
Contributor

@Namoshek Namoshek commented Sep 6, 2016

Purpose

REST API description for the service that allows for sharing of configuration snippets.

Additional info

I checked the format with Drafter and generated a HTML documentation with the Apairy CLI.

TODO

  • Authentication endpoint
  • Database endpoint

Checklist

  • commit messages are fine (with references to issues)
  • I ran all tests and everything went fine
  • meta data is updated (e.g. README.md of plugins)

@markus2330 please review my pull request

@Namoshek
Copy link
Contributor Author

Namoshek commented Sep 6, 2016

So far I was unable to find a way to put schemata into a variable or something similar, so I copied it for each response. But I really don't like it doing this way. Maybe @omnidan has an idea?

@markus2330
Copy link
Contributor

What was the output of drafter (warnings?) and is the HTML docu available somewhere?


## Authenticate [POST]

The API will try to authenticate the user by the submitted credentials. In case of success a session token (JWT) will be returned, which can be used for further requests to the API.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

linebreaks?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why JWT?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

API Blueprints need an empty line between +Body and the example output, for example. So in order to have a more general layout, I decided to use 3 line breaks in front of each first level heading, 2 line breaks in front of each second heading, and so on.

JWT is what i decided to use, because it is very convenient to use in combination with SPA-frontends. And it actually doesn't make a difference for the user, he just has to know how to submit it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, but isn't JWT the cause of the dependency problems? Did you look into the build-in auth from cppcms? Does JWT work with --cookie from curl? Maybe we open a seperate issue for authentification?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The JWT can easily be added to the Authorization header via curl, I did that before. I also added functionality so you can use it as additional GET parameter (?token=the_session_cookie) in case a separate header is not wanted.

Hence I don't really see it as dependency problem at all. I'm using the Boost library for other things as well (e.g. string splitting) and the Cryptlite "library" is only a header collection providing a more natural interface for cryptographic tasks.

But no, I didn't really look for CppCMS authentication stuff, but I also don't expect it to have some. Documentation doesn't give hints about it at all.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dependency problem Cryptlite

Yes, the problem is also not really "ours", but its a problem for every distribution. E.g. in Debian maintainers need to write a licence file where every copied source file must be described. Thus we should avoid distributing source files of other licences/authors. Furthermore it creates huge problems if there are security bugs. cppcms provides crypto and also much more.

Boost is ok if you need more than provided by [booster](http://cppcms.com/cppcms_ref/latest/namespacebooster.html and especially "booster::locale", the author of cppcms is also author of boost::locale, where a full copy is included in booster). The only reason against boost is the bloat of the binary size, because cppcms already uses an internal version of boost and if we use one more version the binary size grows again. But you can keep it, if you do not find an booster replacement in every case. (One problem of boost is that they do not really care about compatibility, please avoid these parts)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For crypto it is required to compile CppCMS with more dependencies (e.g. OpenSSL), so it would only be a shift of dependencies in my eyes. There is also no documentation on how to use it anywhere (yes I looked this up before).

The main problem I have with getting rid of the Cryptlite lib is that using cryptographic functions requires lots of initialization and stuff that is done by this library. So I would basically have to write it on my own again, achieving nothing else than work. Why should I do that? License problems of other people are not my business, to be honest.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For crypto it is required to compile CppCMS with more dependencies (e.g. OpenSSL), so it would only be a shift of dependencies in my eyes. There is also no documentation on how to use it anywhere (yes I looked this up before).

Cppcms on debian is already compiled with libgcrypt11, are there any problems with how it is compiled? It is not only a shift of dependencies. When we use libgcrypt, security bugs are libgcrypt's problems, if we distribute source, every problem is our problem. And cppcms even seems to have different security backends (similar to what @petermax2 made for Elektra's crypto plugin), i.e. if libgcrypt support end, we can switch easily.

And what do you mean with no docu?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I read on sessions in CppCMS before, but they are cookie based only. I've never seen cookie sessions been used for any other API before, so I doubt this is good design..? Of course it would be more comfortable because it is there already, but there must be a reason why APIs use JWTs, oAuth and other mechanism in favour of cookies.

I understand your problem with the dependencies and I'll see what I can do about it, but I'm not promising anything. I was talking about crypto docu by the way, not sessions. I'm using crypto also for other taks (e.g. password hashing). The only docu I could find is the generated doxygen docu that doesn't give much info at all.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe you can find out why they prefer other methods, just because others are doing it is not a good reason.

I'll see what I can do about it

Thank you.

@Namoshek
Copy link
Contributor Author

Namoshek commented Sep 6, 2016

What was the output of drafter (warnings?) and is the HTML docu available somewhere?

Drafter ends with OK. I uploaded the current HTML version to my server.

"type": "string"
},
"rank": {
"description": "The authenticated users rank",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rank?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The rank is used for a very basic permission system. It allows to set a rank between 1 and 3, whereas 1 is a user, 2 something like a moderator and 3 an admin. Higher ranks will allow to delete and modify content of other users as well.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you, can you add this to the API description, too? (Maybe as enum?)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed in the upcoming version, ty for the catch!

…on for possible messages, missing API functions)
@Namoshek
Copy link
Contributor Author

Namoshek commented Sep 7, 2016

I pushed a new version of the API description with a lot more details and better syntax. I also updated the rendered version on my server. As soon as the API works, we could add the host to the blueprint, which would add console support to the rendered documentation. Maybe it would also be a good idea to return the docu instead of the current plain html API description when accessing the API root?

@markus2330
Copy link
Contributor

Generated API looks nice (it seems to be located on apiary.io though)

Maybe it would also be a good idea to return the docu instead

A CSV like format (or JSON if you prefer it) with links to docu and versions for every method would be another way? Returning the whole docu seems to be hardly usable, or do you mean such a Apiary.Embed like it is done on your web server currently?

@omnidan Can you also take a look at the API?

@markus2330
Copy link
Contributor

Two small questions:

  • What are the scopes for? (Database)
  • Why is DELETE only possible for admin? Why should a user not be able to remove what he/she uploaded before?

@Namoshek
Copy link
Contributor Author

Namoshek commented Sep 7, 2016

Generated API looks nice (it seems to be located on apiary.io though)

Where have you seen anything about Apiary in the generated docs?

A CSV like format (or JSON if you prefer it) with links to docu and versions for every method would be another way? Returning the whole docu seems to be hardly usable, or do you mean such a Apiary.Embed like it is done on your web server currently?

I was talking about a generated doc like on my server at the moment. I don't think that REST services normally offer something like a WSDL. Of course I could just return the blueprint, but I think the generated doc is more helpful?

What are the scopes for? (Database)

Basically the idea was that it is easier searchable if there is some very crudely grouping involved in the highest hierarchy level, but with tagging (which idea came a bit later) it is probably not as useful anymore. Do you think I should remove this level?

Why is DELETE only possible for admin? Why should a user not be able to remove what he/she uploaded before?

It is only a docu mistake, of course the user should be able to delete own stuff. Thanks for the catch! 👍

@markus2330
Copy link
Contributor

Where have you seen anything about Apiary in the generated docs?

My browser tells me if content from other domains is loaded.

generated doc is more helpful?

Yes, if its similar to what you have on your webpage, it sounds useful. But then it is not really API anymore. There should be also some programmatic way to get some information, at least the version of the API.

Do you think I should remove this level?

This is a difficult question and depends on how it is solved in the web ui. Basically it is a question of user experience: If it is easy to use the scopes, and it is helpful for retrieving snippets, we should have it. If users are annoyed by it and only use tags anyway, it is better if we do not have it. Maybe the following question helps you for deciding: Will it be clear for a user when to use scopes and when to use tags?

@Namoshek
Copy link
Contributor Author

Namoshek commented Sep 7, 2016

My browser tells me if content from other domains is loaded.

Interesting. Well, it is generated by the Apiary-CLI, so it is quite likely that it is the same sort of documentation than they use for their service. There are other tools available too, I can try some of them as well.

Yes, if its similar to what you have on your webpage, it sounds useful. But then it is not really API anymore. There should be also some programmatic way to get some information, at least the version of the API.

To @omnidan you said version for the API is not really that important...?!

This is a difficult question and depends on how it is solved in the web ui. Basically it is a question of user experience: If it is easy to use the scopes, and it is helpful for retrieving snippets, we should have it. If users are annoyed by it and only use tags anyway, it is better if we do not have it. Maybe the following question helps you for deciding: Will it be clear for a user when to use scopes and when to use tags?

I guess yes, but without a good set of possible scopes it will be hard to find the right one. So I will get rid of it.

@markus2330 markus2330 mentioned this pull request Sep 7, 2016
@Namoshek
Copy link
Contributor Author

Namoshek commented Sep 8, 2016

My browser tells me if content from other domains is loaded.

Interesting. Well, it is generated by the Apiary-CLI, so it is quite likely that it is the same sort of documentation than they use for their service. There are other tools available too, I can try some of them as well.

I tried the aglio doc generator which offers two different templates, both can be found on my server:

It works quite well too and the layout seems nice as well, but one or two things work different (e.g. many {?optional} parameters are not converted into ?param1&param2&param3, but rather in to ?param1?param2?param3. Biggest problem is though that he ommits a lot of output for the request attributes and parameters for example. That is a lot metter with the Apiary CLI.

@omnidan
Copy link
Contributor

omnidan commented Sep 8, 2016

So far I was unable to find a way to put schemata into a variable or something similar, so I copied it for each response. But I really don't like it doing this way. Maybe @omnidan has an idea?

I haven't done this either, but the api-blueprint examples have helped me a lot when writing blueprints before. Maybe you can find something similar there?

If it is easy to use the scopes, and it is helpful for retrieving snippets, we should have it. If users are annoyed by it and only use tags anyway, it is better if we do not have it.

I think it will be hard to define scopes for us if we want to make them predefined. If they're user defined, I would definitely go for tags instead (much easier to maintain, more flexible, most snippets don't have one category, e.g. apache, php, wordpress)

@omnidan Can you also take a look at the API?

I just had a look at the API, here are my comments:

  • Authentication endpoint could be a /user resource (more RESTful)
    • especially setrank is a little weird for a REST API (could be PUT /user/:id to edit a certain user or PUT /user to edit your own account)
    • /auth is fine, because it's special (and /user/auth might conflict with /user/:id), /register could simply be POST /user, though
  • why does the HTTP status code need to also be part of the response body? (redundancy?)
  • Database endpoint is basically the Snippets endpoint? (where you get snippets from other users) Maybe a different name would be more appropriate here?

@Namoshek
Copy link
Contributor Author

Namoshek commented Sep 9, 2016

I haven't done this either, but the api-blueprint examples have helped me a lot when writing blueprints before. Maybe you can find something similar there?

Of course I found and used them already. It is possible to replace some code (e.g. the attributes of responses) by a model, but for the different i18n descriptions I'd have to define this one attribute every time either way. And if I replace the other 3 lines for example, the i18n is afterwards always on top, because the substitution for the model is appended to the list instead of prepended... that doesn't look nice. So for now I kept it quite c&p like. Most important would be a solution to only store schemata once, because they are freaking large, but this seems impossible.

Edit: After some trying I found out that schema declaration seems not necessary at all. When generating the doc with the apiary-cli, schema descriptions are generated automatically. That saves a lot of lines.

I think it will be hard to define scopes for us if we want to make them predefined. If they're user defined, I would definitely go for tags instead...

Thanks for your opinion, I'll go with tags instead!

  • Authentication endpoint could be a /user resource (more RESTful)
    • especially setrank is a little weird for a REST API (could be PUT /user/:id to edit a certain user or PUT /user to edit your own account)
    • /auth is fine, because it's special (and /user/auth might conflict with /user/:id), /register could simply be POST /user, though

Good point there. But concept wise (and also logically) I'm not so sure if it is a good idea to open some operations for the /user resource to the public (e.g. POST for registration) and some not (e.g. PUT for setrank/edit). But I could imagine that a mix of both, /auth and /auth/register like now and the rest like you suggested, could work quite well. Of course I could also create a POST /user resource that is only applicable for admins and that is used by /auth/register internally...

why does the HTTP status code need to also be part of the response body? (redundancy?)

That was part of the decision to put a textual description of the status in the response body as well. Theoretically this should be unnecessary, that is right. I also cannot really think of any reason why it should be necessary, all JS (front-end) libs that I know and use can handle headers and the response codes there. I think I'll remove them if no one can give me a good reason to not do so.

Database endpoint is basically the Snippets endpoint? (where you get snippets from other users) Maybe a different name would be more appropriate here?

Suggestion? 😄
Internally I'm often using the word repository, which seems equally in-/appropriate to me. snippets doesn't really sound that well either to be honest. It is also not really the name of a resource, but rather a prefix to avoid name collission (see /auth and /user). Therefore the naming is not too important in my eyes, but of course I'm open for suggestions!

@omnidan
Copy link
Contributor

omnidan commented Sep 25, 2016

If you use 204 for PUT/DELETE, then you should definitely use 201 for POST as you are already using more specific response codes.

Alright, I changed that.

My PUT/DELETE requests also return a response with a i18n string when they succeed. It is not entirely necessary to be honest, but somehow I think it is better to have a common way to transport i18n keys than mixing them within frontend and backend. If I would return an empty response, I'd have to define the success key in the frontend and error keys in the backend, that doesn't seem to be a good design. What do you think?

You're right. Although, for my API, adding an i18n field everywhere doesn't make much sense as I never have a "success message", I only care about the data and errors in case they happen.

I tried it this way, but there are several problems:

Ah, I see, I thought it was inheriting the type. Re-writing only the i18n value:

        + i18n (string) - a unique token representing above error description message; can be used for internationalization in frontends

fixes all the issues except the dividing line (I don't think it's so bad that it's at the beginning), which seems to be a bug in apiary. I think that's still more readable than copy&pasting all of the attributes.

@Namoshek
Copy link
Contributor Author

Ok, I changed it as well. I hope it will not cause troubles with Dredd later on.

@@ -1,4 +1,5 @@
FORMAT: 1A
HOST: api.libelektra.org
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe "webapi.libelektra.org" or "restapi" is better?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I only added it as placeholder for the moment. I'll have to change it to some localhost url for testing, that's why I needed it. We can change it when the API and the deployment decisions are final.

@markus2330 markus2330 mentioned this pull request Sep 26, 2016
7 tasks
@Namoshek Namoshek changed the title REST API: description draft for sharing of conf snippets REST API: sharing of configuration snippets Oct 10, 2016
@markus2330
Copy link
Contributor

Build seems to fail?

Please remove "work in progress" if it is ready to review.

@Namoshek
Copy link
Contributor Author

ELEKTRA CHECK FORMATTING

diff --git a/src/error/exporterrors.cpp b/src/error/exporterrors.cpp
index 63f79f9..63c9a64 100644
--- a/src/error/exporterrors.cpp
+++ b/src/error/exporterrors.cpp

Must be something broken on the master branch, can't be my fault.

I changed the host, so lets see if the tests still fail.

@markus2330 markus2330 merged commit 18db87b into ElektraInitiative:master Nov 29, 2016
@markus2330
Copy link
Contributor

And the last piece! Thanks!

@Namoshek Namoshek deleted the rest-service-api-desc branch December 9, 2016 14:40
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants