codidact / core

The core Codidact Q&A software implementation
GNU Affero General Public License v3.0
259 stars 20 forks source link

Getting the community scope #22

Open luap42 opened 4 years ago

luap42 commented 4 years ago

I think we need to have this before being able to work on posts and users:

https://github.com/codidact/core/projects/1#card-31579420

A system, which parses the URL, gets the community ID from it, verifies, whether that is in the DB and if it is, loads some information (name, tagline) into memory and passes that to the further controllers and views.

I'd do it, but I don't have enough knowledge in ASP.NET Core, so I'd like to ask for volunteers doing it. :)

Please reply here, when you can do this and I'll assign this task to you.

benjamin-allen commented 4 years ago

I can take a stab at it. Are you envisioning this code running before every controller?

misha130 commented 4 years ago

https://github.com/codidact/core/issues/21 is blocking this

luap42 commented 4 years ago

Most. There'll be some "neutral" pages, such as authentication, administration and community management (add, edit, remove), though.

benjamin-allen commented 4 years ago

Most. There'll be some "neutral" pages, such as authentication, administration and community management (add, edit, remove), though.

That should be possible. I'll make a base controller class that performs the URL parsing you describe. Controllers that don't want that functionality just don't inherit from that base class.

21 is blocking this

I'm not sure why that's the case. The way I'm reading it is that this issue relates to only community data and that issue is concerned with user auth. Am I missing something?

misha130 commented 4 years ago

I'm not sure why that's the case. The way I'm reading it is that this issue relates to only community data and that issue is concerned with user auth. Am I missing something?

I misread the issue at hand. I think we are talking about only displaying in part of a page the community tagline and name. Just need to create a PartialView/View for this task that checks the URL and displays the community based on that. You don't need to create base controllers or anything like that.

asynts commented 4 years ago

If my understanding is correct, then the consensus was to use subdomains. This would require a bit more configuration that is currently documented in README.md.

IIS Express (that is the server thingy that Visual Studio uses) only binds to e.g. https://localhost:8080 by default, but not to e.g. https://foo.localhost:8080. Thus to develop and test this service scope middleware or whatever, a more sophisticated setup is required.

I've tried to come up with something myself, and found this stack overflow answer which seemed promising, but didn't work for me.

Does anyone know how to configure this on Windows?


I've managed to set this up on Linux as follows:

  1. Edit /etc/hosts and add:

    127.0.0.2    codidact.local
    127.0.0.2    foo.codidact.local
    127.0.0.2    bar.codidact.local
  2. Edit src/WebUI/Properties/launchSettings.json as follows:

    {
      "profiles": {
        "WebUI": {
          "commandName": "Project",
          "launchBrowser": true,
          "applicationUrl": "https://codidact.local",
          "environmentVariables": {
            "ASPNETCORE_ENVIRONMENT": "Development"
          }
        }
      }
    }
manassehkatz commented 4 years ago

My recommendation (I think I made it clear in the forum) is that the Codidact software should support both methods, while the Codidact primary instance should (normally, see exception) use subdomains:

The code should care very little about this. It effectively becomes a "filter" of sorts on input and output - like Apache RewriteRule, except managed to a certain degree (I know it can be done, just not exactly how) within the software.

ranolfi commented 4 years ago

Do we really need to implement anything at the application level to manage this? AFAIK it's usually done at the server level (URL Rewrite in Apache, IIS or whatever else).

It wasn't long ago that I refactored out some old URL rewriting code from a legacy .NET web application and ported all rules to IIS instead. Worked just as well, if not better. Very nice to maintain and everyone was happy.

asynts commented 4 years ago

Do we really need to implement anything at the application level to manage this? AFAIK it's usually done at the server level (URL Rewrite in Apache, IIS or whatever else).

The rewriting should only happen if some configuration option is turned on.

If we put this option into the server configuration, it might be necessary to get that information in the application too for e.g. redirects. This would mean redundancy or some common configuration file which seems difficult.


I am also unsure how much the server configuration has to offer, there is also a URL Rewriting Middleware in ASP.NET integrated, but it's not powerful enough to do this rewriting (at least to my knowledge).

ranolfi commented 4 years ago

The rewriting should only happen if some configuration option is turned on.

Is this requirement specified somewhere? I must have missed it (and, in principle, don't agree with).

If we put this option into the server configuration, it might be necessary to get that information in the application too for e.g. redirects. This would mean redundancy or some common configuration file which seems difficult.

In terms of KISS, I very much doubt it will be more difficult than implementing and specially maintaining the middleware in the long term.

I am also unsure how much the server configuration has to offer

A lot! More than enough for the current needs (that I'm aware of).

I maintain that the server configuration is the right place to do this. When there is a requirement for localizing URLs, a middleware might be considered for that specific purpose.

asynts commented 4 years ago

Your formatting is broken, you have to put a newline after a quote, otherwise it is lazily continued.

Is this requirement specified somewhere? I must have missed it (and, in principle, don't agree with).

There was some discussion here, and some stuff further down the thread, leading to this post.

The big plus is that we can use subdomains on https://codidact.com while allowing other instances to use the path schema.

A lot! More than enough for the current needs (that I'm aware of).

I've never done rewriting in a server configuration, maybe one simple regex but nothing more. Maybe someone could provide an example of how this could be done. I got the suspicion that this results in one mega regex that is completely unreadable.

ranolfi commented 4 years ago

Your formatting is broken, you have to put a newline after a quote, otherwise it is lazily continued.

Got it, ty.

Let me follow up on those and come back later to comment. :)

asynts commented 4 years ago

@ranolfi I changed my position on this topic. Don't get me wrong, on the long term doing it with configuration options in C# would is preferable in my opinion, however, we are creating a minimum viable product, thus simpler is better.

I've come up with the following configuration for Nginx:

events {
}

http {
    server {
        listen 80;
        server_name localhost;

        location / {
            proxy_pass https://127.1.0.1:8080/;
        }
    }

    server {
        listen 80;
        server_name ~^(?<community>\w+)\.localhost$;

        location / {
            proxy_pass https://127.1.0.1:8080/community/$community$uri;
        }
    }
}

I am sure something similar can be done in IIS too, but I think someone with Windows knowledge should address that. This can also be done at a later date, because we'd use the path schema during development anyway.

I'll close #23 and salvage some of the configuration stuff in a separate pull request, unless anyone objects.

asynts commented 4 years ago

I edited the configuration to use encryption and added the redirect:

events {
}

http {
    ssl_certificate certs/cert.pem;
    ssl_certificate_key certs/key.pem;

    server {
        listen 443 ssl;
        server_name localhost;

        location ~^/community/(?<community>\w+)(?<path>.*?)$ {
            return 301 https://$community.localhost$path;
        }

        location / {
            proxy_pass https://127.1.0.1:8080/;
        }
    }

    server {
        listen 443 ssl;
        server_name ~^(?<community>\w+)\.localhost$;

        location / {
            proxy_pass https://127.1.0.1:8080/community/$community$request_uri;
        }
    }
}
misha130 commented 4 years ago

For anyone that wants to actually do it here is what you need to do:

! PSUEDO CODE WARNING ! Add a routing option in the startup, example:

       app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "/community/{community}/{controller}/{action}/{id?}");
            });

Add the global filter if appropriate:

                    var routerData  = ....
                    var isCommunityable= entity.FindProperty(nameof(ICommunityable.CommunityId));
                    var parameter = Expression.Parameter(entity.ClrType, "p");
                    var equalExpression = Expression.Equal(
                            Expression.Property(parameter, isCommunityable.PropertyInfo),
                            Expression.Constant(routerData["Community"])
                        );
                    var filter = Expression.Lambda(equalExpression, parameter);
                    entity.SetQueryFilter(filter);