Base solution for your next web application
Open Closed

Adding URL/HOST/Tenant restrictions to site access #9007


User avatar
0
smry created

We have our site on mysite.com/www.mysite.com As of now, we added A records such that mysite.com goes to a wordpress site and * goes to ASPNETZero site

I am trying to setup a connection workflow so that non-tenant links redirect to wordpress instead of host Also, I need to leave open a subdomain "portal" for admins to access, Im assuming through a specific subdomain since we're restricting access to all 'failed' subdomains

I believe this is a table that explains the cases: | URL | outcome | current status | | --- | --- | --- | | mysite.com | wordpress site | working | | client.mysite.com | tenant site | working | | admin.mysite.com | Host site, IP restricted | 'works', but needs IP restrictions | | [noTenant].mysite.com | redirect to wordpress site | not working |

Looking in the API I see that the WebUrlServiceBase.cs seems to be where some of this takes place Unfortunately I am unable to debug in local and the routes only seem active in live

I found this in github which only is a local not external redirect on fail: https://github.com/aspnetboilerplate/aspnetboilerplate/issues/3958

Also, I think a possible solution would be in RedirectToExternalLoginPageAsync or something within the AccountController all together where I would test if tenant exists, if not go to mysite.com. I couldn't find specific information on rerouting within the application.

I was wondering if you had suggestions on what my approach could be?


5 Answer(s)
  • User Avatar
    1
    maliming created
    Support Team

    If the url contains the tenant name but the tenant does not exist, you can redirect it in the front-end application.

    eg: noTenant.mysite.com

    noTenant exists in the url, but tenant does not exist, then redirect to mysite.com.

    For angular, AppPreBootstrap.ts will be an entry point. For MVC, you can consider using js script.

  • User Avatar
    0
    smry created

    We're using Angular2/.NETCore setup and I found the AppPreBootstrap.ts file

    I frankensteined a new method within AppPreBootstrap.run:

    [...]  const queryStringObj = UrlHelper.getQueryParameters();
    
    // Go to External site if tenant doesn't exist
    if (AppPreBootstrap.checkTenant(() => { AppPreBootstrap.getUserConfiguration(callback); })) {
        window.location.href = environment.siteRoute;
    }
    
    if (queryStringObj.redirect && queryStringObj.redirect === 'TenantRegistration') {  [...]
    
    // New check method
    private static checkTenant(callback: () => void): boolean {
            let returnVal = false;
            console.log('checkTenant');
            const subdomainTenancyNameFinder = new SubdomainTenancyNameFinder();
            const tenancyName = subdomainTenancyNameFinder.getCurrentTenancyNameOrNull(AppConsts.appBaseUrl);
            let input = new IsTenantAvailableInput();
            input.tenancyName = tenancyName;
    
            let requestHeaders = AppPreBootstrap.getRequetHeadersWithDefaultValues();
    
            XmlHttpRequestHelper.ajax(
                'POST',
                AppConsts.remoteServiceBaseUrl + '/api/services/app/Account/IsTenantAvailable',
                requestHeaders,
                input,
                (response) => {
                    let result = response.result;
                    switch (result.state) {
                        case TenantAvailabilityState.Available:
                            returnVal = false;
                            break;
                        case TenantAvailabilityState.InActive:
                            returnVal = false;
                            break;
                        case TenantAvailabilityState.NotFound:
                            returnVal = true;
                            break;
                    }
                    callback();
                }
            );
            return returnVal;
        }
    

    Failed to load resource: the server responded with a status of 400 (Bad Request) [http://localhost:19945/api/services/app/Account/IsTenantAvailable?d=1589320901545]

    I kept trying with no success so I deciced to put the reroute code somewhere else. I thought app-session.service would be a good place and modified init:

    init(): Promise<UiCustomizationSettingsDto> {
    
            if (!AppConsts.tenantName || this.checkTenant()) {
                window.location.href = environment.siteRoute;
            }...
    
    
    ------------
    
    
    checkTenant(): boolean {
        let returnVal = false;
        let input = new IsTenantAvailableInput();
        input.tenancyName = AppConsts.tenantName;
    
        this._accountService.isTenantAvailable(input)
            .subscribe((result: IsTenantAvailableOutput) => {
                switch (result.state) {
                    case TenantAvailabilityState.NotFound: //NotFound
                        window.location.href = environment.siteRoute;
                        break;
                }
            });
        return returnVal;
    }
    

    I ended up with this which seems to do what I need it to

  • User Avatar
    0
    smry created

    @maliming - everything works except I cant access Host account any longer I noticed that the Host account on main database had tenant name of 'Default' so I assumed that would take me to Host account default.mysite.com just takes me to a tenant called default with No Host permissions

    In app-session.service.ts I updated isCurrentTenant method:

    private isCurrentTenant(tenantId?: number) {
            let isTenant = tenantId > 0;
    
            if (tenantId === 1 && AppConsts.tenantName.toLowerCase() === 'default') { // this is new host
                return true;
            } ...
    

    I am unsure where I can alias my host account - now that http://mysite.com goes to my wordpress site

  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi,

    Is admin.mysite.com and client.mysite.com referst to same deployment on your server ? If so, you need to create your own version of DomainTenantResolveContributor (see https://aspnetboilerplate.com/Pages/Documents/Multi-Tenancy#determining-current-tenant).

    In your custom class you can return null if tenancy name is "admin" and execute default behaviour if not.

  • User Avatar
    0
    smry created

    So I had the right idea but wrong followthrough

    Because a tenant 'Default' exists it tried to push me there I needed to change my app-session to this:

    init(): Promise<UiCustomizationSettingsDto> {
            if (environment.production && (!AppConsts.tenantName || this.checkTenant())) {
                window.location.href = environment.siteRoute;
            }
    
            return new Promise<UiCustomizationSettingsDto>((resolve, reject) => {
                this._sessionService.getCurrentLoginInformations().toPromise().then((result: GetCurrentLoginInformationsOutput) => {
                    this._application = result.application;
                    this._user = result.user;
                    this._tenant = result.tenant;
                    this._theme = result.theme;
    
                    resolve(result.theme);
                }, (err) => {
                    reject(err);
                });
            });
        }
    
        checkTenant() {
            let input = new IsTenantAvailableInput();
            input.tenancyName = AppConsts.tenantName;
    
            if (AppConsts.tenantName.toLowerCase() === 'portal') {
                return false;
            }
    
            this._accountService.isTenantAvailable(input)
                .subscribe((result: IsTenantAvailableOutput) => {
                    switch (result.state) {
                        case TenantAvailabilityState.Available:
                            console.log('Available');
                            return false;
                        case TenantAvailabilityState.InActive:
                            console.log('InActive');
                            return false;
                        case TenantAvailabilityState.NotFound:
                            console.log('NotFound');
                            window.location.href = environment.siteRoute;
                            return true;
                        default:
                            return false;
                    }
                });
        }
    
        changeTenantIfNeeded(tenantId?: number): boolean {
            if (this.isCurrentTenant(tenantId)) {
                return false;
            }
    
            abp.multiTenancy.setTenantIdCookie(tenantId);
            location.reload();
            return true;
        }
    
        private isCurrentTenant(tenantId?: number) {
            let isTenant = tenantId > 0;
    
            if (AppConsts.tenantName.toLowerCase() === 'portal') { // this is new host
                return true;
            }
    
            if (!isTenant && !this.tenant) { // this is host
                return true;
            }
    
            if (!tenantId && this.tenant) {
                return false;
            } else if (tenantId && (!this.tenant || this.tenant.id !== tenantId)) {
                return false;
            }
    
            return true;
        }