io7m-com / idstore

Identity server
https://www.io7m.com/software/idstore
ISC License
1 stars 0 forks source link

Few Questions #149

Closed cloudcompute closed 6 months ago

cloudcompute commented 6 months ago

Hi @io7m

While browsing the github site, I stumbled upon this repository. The code looks really organised.

For creating instances, I am seeing you are using the new operator, and not using any Dependency Injection library or using Javax @Inject annotation. It is probably because it takes a toll on performance. Is this correct? But there are few dependency injector like Dagger that reads the annotation at compile time and performance is not affected.

Is the code making sure that the service objects should be singletons i.e. only a single object in memory? I didn't encounter the @Singleton annotation.

While most of the models are declared using the record keyword, some models like this are simply defined using the old-fashioned class keyword. You may opt using the Project Lombok library.

Thanks Raman

io7m commented 6 months ago

Hello!

Thanks for your interest.

For creating instances, I am seeing you are using the new operator, and not using any Dependency Injection library or using Javax @Inject annotation. It is probably because it takes a toll on performance. Is this correct? But there are few dependency injector like Dagger that reads the annotation at compile time and performance is not affected.

I use repetoir for dependency injection. It's about mechanical simplicity; no annotation processing, no runtime reflection, and a single place in the code where services are instantiated (so it's extremely easy to see, from an auditing perspective, which implementations are being used). I'm less concerned about performance; the JVM is already stupidly fast. The idstore server goes from zero to serving requests in about 500 milliseconds on hardware here and on various VPS hosting I have in production.

Is the code making sure that the service objects should be singletons i.e. only a single object in memory? I didn't encounter the @singleton annotation.

The code consuming services doesn't care if they're singletons or not. From the perspective of code consuming services, it just asks for an instance of a particular interface and gets it.

While most of the models are declared using the record keyword, some models like this are simply defined using the old-fashioned class keyword. You may opt using the Project Lombok library.

I'm strongly opposed to Project Lombok; it has a tendency to make codebases completely incomprehensible, and it's very difficult to migrate any code away from it once it's in a codebase. The IdPasswordAlgorithmPBKDF2HmacSHA256 class isn't defined as a record because it doesn't have record semantics; it's not a dumb carrier of data.

cloudcompute commented 6 months ago

Thanks for the great answer. I'll try to run it locally.

How many requests in 500 ms locally? Since when is it getting used in production? Does it offer Attribute based access control.. fine grained permission stuff?

io7m commented 6 months ago

How many requests in 500 ms locally?

I don't know how many requests it can sustain in 500ms, I've not benchmarked beyond the initial performance tests I did to ensure that I wasn't doing anything horribly sub-optimal with the database. It uses Helidon Nima for the embedded HTTP server, and PostgreSQL for the database, so performance is essentially defined by those two components.

Since when is it getting used in production?

I've been running it in my own projects for about two years.

Does it offer Attribute based access control.. fine grained permission stuff?

The admin interface uses a capability-based permissions model: https://www.io7m.com/software/idstore/documentation/index.xhtml#id_634db1a2-1705-44ae-abac-8fa281b100f4

The user interface doesn't have permissions because there aren't actually any operations for users to perform; all the server does for users is determine if they're permitted to log in or not. :slightly_smiling_face:

io7m commented 6 months ago

As may or may not be obvious, the server doesn't do all that much that's interesting by itself. It exists because I write a lot of server software, and I didn't want to repeatedly implement password/credential handling in all of the other servers. There were no other pure identity servers I could find that would match the very stringent requirements I have with regard to security, code quality, operational simplicity, protocol simplicity, etc, hence idstore! On its own, idstore is entirely boring. :slightly_smiling_face:

cloudcompute commented 6 months ago

Hi

Thank you for the detail response.

I agree that the idstore's code quality is looking great. I also prefer simple code which is easy to understand.

Here is an IAM written in Java, but the code is complex and it is using Spring which I do not like, its code is over-abstracted.. offers much more than it is needed. Also, performance-wise it is unpleasant. https://github.com/gravitee-io/gravitee-access-management

If you are interested in knowing about the performance of Helidon and other frameworks, please visit this link

At the top two are: Vertx and Jooby. Vertx is reactive and since Java now has Virtual threads, reactive programming will take a back seat. Jooby, at first site, looks great.. but I do not know whether developers using it in production. The best thing in Jooby is it is modular based and we can pick any libraries we want to, like for example.. Netty, Jetty.

io7m commented 6 months ago

Here is an IAM written in Java, but the code is complex and it is using Spring which I do not like, its code is over-abstracted.. offers much more than it is needed. Also, performance-wise it is unpleasant.

Yes, I find Spring is generally a red flag. I've never really understood what problem it's trying to solve. It very much has the feel of "The bureaucracy is expanding to meet the needs of the expanding bureaucracy.". It seems like a lot of the "problems" it "solves" can be solved by not using Spring at all.

If you are interested in knowing about the performance of Helidon and other frameworks, please visit this link

Yeah, I'm vaguely aware of the performance characteristics of Helidon. I followed the development of virtual threads closely and did a lot of testing on the JDK preview releases (although all of the problems I found had already been found by others by the time I reported any of them). idstore actually previously used Jetty, but I was waiting for JDK 21 and Helidon to have production releases so that I could switch to them. Jetty has a lot of baggage related to asynchronous programming as obviously that was previously the only reasonable way to write a server given that the JVM only had platform threads at the time. Again, I'm not particularly concerned about performance; I'm concerned about correctness, readability, long-term maintainability. It's typically far easier for code written in a simple blocking style to have those properties. It's extremely difficult to even grasp the path a request takes from a network socket to the application code in Jetty due to layers of legacy baggage and complexity introduced in the name of performance.

The performance of idstore, somewhat ironically, isn't all that critical: The main operation it exposes to the rest of the world is a login operation and, for security reasons, that operation is typically rate limited by sleeping. If you have ten thousand users all trying to log in concurrently, what you actually get on the server side is ten thousand virtual threads all sleeping for a second or so in order to rate limit. Having ten thousand virtual threads parked and doing nothing is about the easiest possible case for the VM to handle. :laughing:

At the top two are: Vertx and Jooby.

To be honest, I'm really not interested in web frameworks. I consider about 90% of the technology to be a stack of bad solutions to problems the technology itself caused. So many of the problems the frameworks purport to solve can be solved by just ... not doing anything. Even HTTP as a protocol is absurdly complex for what it does: Take a look at the HTTP/1.1 specification! The protocol has only gotten larger and more complicated since then. Netty is actually a prime example of an extremely complicated solution to a problem that's now solved relatively trivially with blocking I/O and virtual threads.

In my experience, it's far better to build something small and bespoke with well-understood and well-defined operational semantics than it is to try to crowbar something large and general purpose into whatever problem you're facing. Unfortunately, there's a pretty widespread belief that one should always use solutions to problems written by others regardless of the cost. It takes confidence in one's own ability and a solid understanding of the problem to do otherwise.

cloudcompute commented 6 months ago

Great ! I will add two cents of mine.. Most of the companies blindly start using the Stack that gets popular.

An ideal example is Spring. I have seen Reddit users stating that they don't want to use Spring but their company wants them to use for the only reason that Spring devs are easy to find. I am following Spring since a decade. They are still sticking to (keep modifying) their legacy Spring framework library.

So the bottom line we should stick to Java JDK as closely as possible.

What you actually get on the server side is ten thousand virtual threads all sleeping for a second or so in order to rate limit. Having ten thousand virtual threads parked and doing nothing is about the easiest possible case for the VM to handle

I didn't get to understand this.. what exactly do you want to say.

io7m commented 6 months ago

So the bottom line we should stick to Java JDK as closely as possible.

Yes, exactly. The VM itself has well-specified semantics, and the fewer layers of crap between it and the actual application, the better.

I didn't get to understand this.. what exactly do you want to say.

If you take a look at how the login operation code works in idstore, there's a configurable fixed delay applied to login requests:

https://github.com/io7m-com/idstore/blob/c0b6bbceeb4cb6a2ce787943da7e6a20a10547a2/com.io7m.idstore.server.user_v1/src/main/java/com/io7m/idstore/server/user_v1/IdU1HandlerLogin.java#L97

The reason for this is to make brute-force password guessing impractical; if the admin has set a one second login delay, then it's obviously only possible for an attacker to try one password per second in serial.

In terms of how this affects performance: What I meant was that most of the time we're thinking of performance in terms of requests handled per second, but the primary operation that the idstore server exposes for users is going to be purposefully limited by configuration. This means that if ten thousand users all turn up at once, there are immediately going to be ten thousand virtual threads doing nothing but sleeping for however long the server has been configured to delay login requests. You could have the fastest database and HTTP server on the planet, but it's still going to take a second to login (if that's how the server has been configured), and the load on the server is going to be close to zero, as the virtual thread for each request is parked and sleeping.

cloudcompute commented 6 months ago

Right, I got it now.

Thanks