Example #1
0
        /// <summary>
        /// Changes a user role within the organization
        /// </summary>
        /// <param name="dbCtx"></param>
        /// <param name="user"></param>
        /// <returns></returns>
        public async Task ChangeOrganizationUserRoleAsync(DbContext dbCtx, MapHiveUser user)
        {
            //this basically needs to remove all the org roles for a user and add the specified one
            var ownerRole = await GetOrgOwnerRoleAsync(dbCtx);

            var adminRole = await GetOrgAdminRoleAsync(dbCtx);

            var memberRole = await GetOrgMemberRoleAsync(dbCtx);

            var addRole = memberRole;

            switch (user.OrganizationRole)
            {
            case OrganizationRole.Admin:
                addRole = adminRole;
                break;

            case OrganizationRole.Owner:
                addRole = ownerRole;
                break;
            }

            user.RemoveLink(ownerRole);
            user.RemoveLink(adminRole);
            user.RemoveLink(memberRole);

            if (user.OrganizationRole.HasValue)
            {
                user.AddLink(addRole);
            }

            await user.UpdateAsync(dbCtx);
        }
 /// <summary>
 /// Gets a full user name based on user data, so pretty much name && surname? name surname : surname || name || email
 /// </summary>
 /// <param name="u"></param>
 /// <returns></returns>
 public static string GetFullUserName(this MapHiveUser u)
 {
     return(!string.IsNullOrEmpty(u.Forename) && !string.IsNullOrEmpty(u.Surname)
         ? $"{u.Forename} {u.Surname}"
         : !string.IsNullOrEmpty(u.Forename)
             ? u.Forename
             : !string.IsNullOrEmpty(u.Surname)
                 ? u.Surname
                 : u.Email);
 }
Example #3
0
        /// <summary>
        /// Adds a member to an organization
        /// </summary>
        /// <param name="dbCtx"></param>
        /// <param name="user"></param>
        /// <param name="role"></param>
        /// <returns></returns>
        public async Task AddOrganizationUserAsync(DbContext dbCtx, MapHiveUser user, OrganizationRole role = OrganizationRole.Member)
        {
            this.AddLink(user);
            await this.UpdateAsync(dbCtx);

            //by default assign a member role to a user
            var memberRole = await this.GetOrgRoleAsync(dbCtx, role);

            user.AddLink(memberRole);
            await user.UpdateAsync <MapHiveUser>(dbCtx);
        }
Example #4
0
        //FIXME - make it possible to pass all the org assets as a param, so can avoid re-reading db!

        /// <summary>
        /// Gets a list of user's organisations that have access to specified apps
        /// </summary>
        /// <param name="dbCtx"></param>
        /// <param name="uuid"></param>
        /// <param name="appShortNames">app short names - when provided, only orgs that contain a listed app are returned</param>
        /// <returns></returns>
        public static async Task <IEnumerable <Organization> > GetUserOrganizationsAsync(DbContext dbCtx, Guid uuid,
                                                                                         IEnumerable <string> appShortNames)
        {
            var user = new MapHiveUser();

            user = await user.ReadAsync(dbCtx, uuid);

            if (user == null)
            {
                return(null);
            }

            var organizations = await user.GetParentsAsync <MapHiveUser, Organization>(dbCtx, detached : true);

            if (organizations == null || !organizations.Any())
            {
                return(new List <Organization>());
            }

            var filteredOrgs = new List <Organization>();

            foreach (var organization in organizations)
            {
                //get the apps linked directly
                organization.Applications = (await organization.GetOrganizationAssetsAsync <Application>((MapHiveDbContext)dbCtx))?.assets.ToList();

                if (appShortNames != null && appShortNames.Any())
                {
                    if (organization.Applications == null ||
                        !organization.Applications.Any(x => appShortNames.Contains(x.ShortName)))
                    {
                        continue;
                    }

                    filteredOrgs.Add(organization);
                }
                else
                {
                    filteredOrgs.Add(organization);
                }
            }

            return(filteredOrgs);
        }
Example #5
0
        /// <summary>
        /// Creates an org owner account - creates a user profile, an organization for a user, ties all the bits and pieces together
        /// </summary>
        /// <param name="dbCtx"></param>
        /// <param name="input"></param>
        /// <param name="emailSender"></param>
        /// <returns></returns>
        public static async Task <AccountCreateOutput> CreateAccountAsync(
            MapHiveDbContext dbCtx,
            AccountCreateInput input,
            IEmailSender emailSender
            )
        {
            var user = new MapHive.Core.DataModel.MapHiveUser()
            {
                Email    = input.AccountDetails.Email,
                Slug     = input.AccountDetails.Slug,
                Forename = input.AccountDetails.Forename,
                Surname  = input.AccountDetails.Surname,

                Company      = input.AccountDetails.Company,
                Department   = input.AccountDetails.Department,
                ContactPhone = input.AccountDetails.ContactPhone
            };


            //now the org object
            var orgNameDesc = string.IsNullOrEmpty(input.AccountDetails.Company) ? input.AccountDetails.Email + "-org" : input.AccountDetails.Company;
            var newOrg      = new Organization
            {
                DisplayName = orgNameDesc,
                Description = orgNameDesc,
                Slug        = Utils.Slug.GetOrgSlug(orgNameDesc, user.Slug + "-org"),

                //push to extra billing info?
                BillingExtraInfo = new SerializableDictionaryOfString
                {
                    { nameof(input.AccountDetails.ContactPhone), input.AccountDetails.ContactPhone },
                    { nameof(input.AccountDetails.Email), input.AccountDetails.Email },
                    { "ContactPerson", $"{input.AccountDetails.Forename} {input.AccountDetails.Surname}" },
                    { nameof(input.AccountDetails.Street), input.AccountDetails.Street },
                    { nameof(input.AccountDetails.HouseNo), input.AccountDetails.HouseNo },
                    { nameof(input.AccountDetails.FlatNo), input.AccountDetails.FlatNo },
                    { nameof(input.AccountDetails.Postcode), input.AccountDetails.Postcode },
                    { nameof(input.AccountDetails.City), input.AccountDetails.City },
                    { nameof(input.AccountDetails.Country), input.AccountDetails.Country },
                    { nameof(input.AccountDetails.VatNumber), input.AccountDetails.VatNumber }
                },

                LicenseOptions = new OrganizationLicenseOptions()
            };


            //got the user and org, o need to validate them now in order to ensure they are creatable
            if (user.Slug == newOrg.Slug)
            {
                var ex = new ValidationFailedException();
                ex.ValidationErrors.Add(new ValidationError
                {
                    Message      = $"MapHiveUser slug already taken: {user.Slug}",
                    Code         = "user_org_slug_duplicate",
                    PropertyName = nameof(MapHiveUser.Slug)
                });

                throw ex;
            }

            //validate user and org - both should throw if a slug is already taken
            await user.ValidateAsync(dbCtx);

            await newOrg.ValidateAsync(dbCtx);


            //prepare the email template tokens known at this stage,
            var tokens = new Dictionary <string, object>
            {
                { "UserName", $"{user.GetFullUserName()} ({user.Email})" },
                { "Email", user.Email }
            };

            //and create user
            var accountCreateOutput = await MapHive.Core.DataModel.MapHiveUser.CreateUserAccountAsync(dbCtx, user, emailSender,
                                                                                                      input.EmailAccount, input.EmailTemplate?.Prepare(tokens));

            user = accountCreateOutput.User;



            //continue with the org setup

            //see what apps the client api wants to register
            var appIdentifiers = input.LicenseOptions.Keys.ToArray();
            //get them...
            var apps = await dbCtx.Applications.Where(a => appIdentifiers.Contains(a.ShortName)).ToListAsync();

            //always make sure to glue in the core apis and apps
            apps.AddRange(
                MapHive.Core.Defaults.Applications.GetDefaultOrgApps()
                );

            foreach (var appShortName in input.LicenseOptions.Keys)
            {
                var app = apps.FirstOrDefault(a => a.ShortName == appShortName);
                if (app == null)
                {
                    continue;
                }

                newOrg.LicenseOptions.Add(
                    new OrganizationLicenseOption
                {
                    LicensedObjectTypeUuid = app.TypeUuid,
                    LicensedObjectUuid     = app.Uuid,
                    LicenseOptions         = input.LicenseOptions[appShortName]
                }
                    );
            }


            //create an org with owner and register the specified apps
            //make sure though to use the collection that contains the the default org apps!
            await newOrg.CreateAsync(dbCtx, user, apps);

            //wire up user with his 'parent org'
            //this is so it is clear a user has his own org
            user.UserOrgId = newOrg.Uuid;
            await user.UpdateAsync(dbCtx);

            return(new AccountCreateOutput
            {
                EmailTemplate = input.EmailTemplate,
                VerificationKey = accountCreateOutput.VerificationKey,
                InitialPassword = accountCreateOutput.InitialPassword
            });
        }
Example #6
0
 /// <summary>
 /// Checks if a user is an organization admin (user has the org admin role assigned)
 /// </summary>
 /// <param name="dbctx"></param>
 /// <param name="user"></param>
 /// <returns></returns>
 public async Task <bool> IsOrgAdminAsync(DbContext dbctx, MapHiveUser user)
 {
     return(await user.HasChildLinkAsync(dbctx, await GetOrgAdminRoleAsync(dbctx)));
 }
Example #7
0
 /// <summary>
 /// Determines if a user is an org member (is assigned to an org)
 /// </summary>
 /// <param name="dbctx"></param>
 /// <param name="user"></param>
 /// <returns></returns>
 public async Task <bool> IsOrgMemberAsync(DbContext dbctx, MapHiveUser user)
 {
     return(await this.HasChildLinkAsync(dbctx, user));
 }
Example #8
0
        /// <summary>
        /// Creates and organisation account, register a user as an owner and registers the specified apps to be linked to it too
        /// </summary>
        /// <param name="org"></param>
        /// <param name="dbCtx"></param>
        /// <param name="owner"></param>
        /// <param name="appsToLink"></param>
        /// <returns></returns>
        public static async Task <Organization> CreateAsync(this Organization org, DbContext dbCtx, MapHiveUser owner, IEnumerable <Application> appsToLink)
        {
            //first create the org
            await org.CreateAsync(dbCtx);

            //take care of assigning the owner role to a user
            await org.AddOwnerAsync(dbCtx, owner);

            //finaly grab the apps that should be registered for the org and link them
            var mhDb = (MapHiveDbContext)dbCtx;

            foreach (var app in appsToLink)
            {
                org.AddLink(app);
            }

            await org.UpdateAsync(dbCtx, org.Uuid);

            return(org);
        }
Example #9
0
        /// <summary>
        /// Creates and organisation account, register a user as an owner and registers the specified apps to be linked to it too
        /// </summary>
        /// <param name="org"></param>
        /// <param name="dbCtx"></param>
        /// <param name="owner"></param>
        /// <param name="appsToLink"></param>
        /// <returns></returns>
        public static async Task <Organization> CreateAsync(this Organization org, DbContext dbCtx, MapHiveUser owner, IEnumerable <string> appsToLink)
        {
            //finaly grab the apps that should be registered for the org and link them
            var mhDb = (MapHiveDbContext)dbCtx;

            var apps = await mhDb.Applications.Where(app => appsToLink.Contains(app.ShortName))
                       .OrderBy(app => app.ShortName)
                       .ToListAsync();

            return(await CreateAsync(org, dbCtx, owner, apps));
        }
        /// <summary>
        /// Creates a user acount, sends out email, modifies pass if a custom pass is provided;
        /// this is a simple wrapper over the standard user.CreateAsync that adds an option to provide a specific password
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="dbCtx"></param>
        /// <param name="user"></param>
        /// <param name="emailSender"></param>
        /// <param name="emailAccount"></param>
        /// <param name="emailTemplate"></param>
        /// <param name="password"></param>
        /// <returns></returns>
        public static async Task <CreateUserAccountOutput> CreateUserAccountAsync(DbContext dbCtx,
                                                                                  MapHiveUser user,
                                                                                  IEmailSender emailSender,
                                                                                  IEmailAccount emailAccount   = null,
                                                                                  IEmailTemplate emailTemplate = null, string password = null)
        {
            var output = new CreateUserAccountOutput
            {
                User = user
            };

            //see if a user has already been created. if so do not attempt to create it;
            if (user.Uuid != default(Guid))
            {
                return(output);
            }

            //need to grab an initial pass to change it if a pass has been provided
            var initialPass = string.Empty;


            //wire up an evt listener, so can react to user created evt and send a confirmation email
            user.UserCreated += (sender, args) =>
            {
                initialPass = (string)args.OperationFeedback["InitialPassword"];

                //output, so can use it in the m2m tests
                output.InitialPassword = initialPass;
                output.VerificationKey =
                    Auth.MergeIdWithToken(
                        user.Uuid,
                        (string)args.OperationFeedback["VerificationKey"]
                        );


                //prepare email if present
                emailTemplate?.Prepare(args.OperationFeedback);

                if (emailTemplate != null && emailAccount != null)
                {
                    emailSender.Send(
                        emailAccount,
                        emailTemplate,
                        user.Email
                        );
                }
            };

            //create user without auto email send here - it's customised and sent via evt handler above
            var createdUser = await user.CreateAsync(dbCtx);

            //once user has been created adjust his pass if provided
            if (!string.IsNullOrEmpty(password))
            {
                //grab user manager
                var userManager = MapHive.Core.Identity.UserManagerUtils.GetUserManager();

                var idUser = await userManager.FindByIdAsync(user.Uuid.ToString());

                await userManager.ChangePasswordAsync(idUser, initialPass, password);
            }

            return(output);
        }
Example #11
0
        /// <summary>
        /// Returns user application credentials within an organization
        /// </summary>
        /// <param name="dbCtx"></param>
        /// <param name="user"></param>
        /// <param name="app"></param>
        /// <returns></returns>
        public async Task <OrgUserAppAccessCredentials> GetUserAppAccessCredentialsAsync(DbContext dbCtx, MapHiveUser user,
                                                                                         Application app)
        {
            var output = new OrgUserAppAccessCredentials
            {
                User         = user,
                Organization = this,
                Application  = app
            };

            //check if organization can access an application and if not, then good bye
            if (!await CanUseAppAsync(dbCtx, app))
            {
                return(output);
            }

            //ignore api apps; non authorized apps are accessible anyway
            //and apis just need to be assigned to an organisation in order to be accessible
            if (app.IsApi)
            {
                output.CanUseApp = true;
                return(output);
            }

            //check if user is an org owner or org admin
            output.IsAppAdmin = await IsOrgOwnerAsync(dbCtx, user) || await IsOrgAdminAsync(dbCtx, user);

            output.CanUseApp = output.IsAppAdmin;


            //user is not granted app admin access via org owner / org admin roles, so need to check if user can access the app as a 'normal' user, so via teams
            //note: teams can also grant app admin role!
            if (!output.IsAppAdmin)
            {
                var orgTeams = await this.GetChildrenAsync <Organization, Team>(dbCtx);

                foreach (var team in orgTeams)
                {
                    //get team's app link
                    var teamAppLink = await team.GetChildLinkAsync(dbCtx, app);

                    //make sure team grants access to an app
                    if (teamAppLink == null)
                    {
                        continue;
                    }


                    //get team's user link
                    var teamUserLink = await team.GetChildLinkAsync(dbCtx, user);

                    var userCanUseApp = teamUserLink != null;

                    //if a team grants an access to an app for a user we can test if it also test if it is admin access
                    if (userCanUseApp)
                    {
                        //only apply true assignment here so it does not get reset when searching for app admin credentials!
                        output.CanUseApp = userCanUseApp;

                        //extract app access credentials link data
                        var appAccessCredentials = teamAppLink.LinkData.GetByKey(Team.AppAccessCredentialsLinkDataObject);


                        output.IsAppAdmin = appAccessCredentials != null &&
                                            appAccessCredentials.ContainsKey(Team.AppAdminAccess) &&
                                            (bool)appAccessCredentials[Team.AppAdminAccess];
                    }

                    //no point in testing other teams, as full app access is already granted
                    if (output.IsAppAdmin)
                    {
                        break;
                    }
                }
            }

            //if this is a default (dashboard) app a user should ALWAYS BE ABLE TO use it; the default (dashboard) app will take care of handling the context itself
            if (app.IsDefault)
            {
                output.CanUseApp = true;
            }

            return(output);
        }
Example #12
0
        /// <summary>
        /// Removes owner from an organisation; this is done by simply removing a link to the admin role
        /// </summary>
        /// <param name="org"></param>
        /// <param name="dbCtx"></param>
        /// <param name="u"></param>
        /// <returns></returns>
        public static async Task RemoveAdminAsync(this Organization org, DbContext dbCtx, MapHiveUser u)
        {
            //first grab the admin role for the org
            var adminR = await org.GetOrgAdminRoleAsync(dbCtx);

            //and remove the link
            var mhDb = (MapHiveDbContext)dbCtx;

            mhDb.Links.Remove(
                mhDb.Links.FirstOrDefault(r => r.ParentUuid == u.Uuid && r.ChildUuid == adminR.Uuid));

            await mhDb.SaveChangesAsync();
        }
Example #13
0
        /// <summary>
        /// Adds a user to an org as an admin.
        /// </summary>
        /// <param name="org"></param>
        /// <param name="dbCtx"></param>
        /// <param name="u"></param>
        /// <returns></returns>
        public static async Task AddAdminAsync(this Organization org, DbContext dbCtx, MapHiveUser u)
        {
            //first find the admin role - it should always be there as it is created on org create
            var adminR = await org.GetOrgAdminRoleAsync(dbCtx);

            //and next link it to a user
            u.AddLink(adminR);
            await u.UpdateWithNoIdentityChangesAsync <MapHiveUser>(dbCtx);

            //and also add a user to an org - this will not change anything if a user is already linked
            org.AddLink(u);
            await org.UpdateAsync(dbCtx, org.Uuid);
        }
        /// <summary>
        ///
        /// </summary>
        /// <param name="org"></param>
        /// <param name="dbCtx"></param>
        /// <param name="u"></param>
        /// <returns></returns>
        public static async Task AddOwnerAsync(this Organization org, DbContext dbCtx, MapHiveUser u)
        {
            //first find the owner role - it should always be there as it is created on org create
            var ownerR = await org.GetOrgOwnerRoleAsync(dbCtx);

            //and next link it to a user
            u.AddLink(ownerR);
            await u.UpdateWithNoIdentityChangesAsync <MapHiveUser>(dbCtx);

            //and also add a user to an org
            org.AddLink(u);
            await org.UpdateAsync(dbCtx);
        }
Example #15
0
        /// <summary>
        /// Gets apps visible by a user
        /// </summary>
        /// <param name="dbCtx"></param>
        /// <param name="userId"></param>
        /// <param name="orgIdentifier"></param>
        /// <returns></returns>
        public static async Task <IEnumerable <Application> > GetUserAppsAsync(DbContext dbCtx, Guid?userId, string orgIdentifier = null)
        {
            var appCollector = new List <Application>();

            //common apps
            //do not return hive apps! they should not be listed in user apps even though they will usually be common apps
            var commonApps = await dbCtx.Set <Application>().Where(a => a.IsCommon && !a.IsHive).ToListAsync();

            appCollector.AddRange(commonApps);


            MapHiveUser user = null;

            if (userId.HasValue)
            {
                user = await dbCtx.Set <MapHiveUser>().FirstOrDefaultAsync(u => u.Uuid == userId);
            }

            Organization org = null;

            if (user != null && !string.IsNullOrEmpty(orgIdentifier))
            {
                org = await dbCtx.Set <Organization>().FirstOrDefaultAsync(o => o.Slug == orgIdentifier);
            }


            //get org apps - the apps that are not public, but assigned to orgs directly
            if (user != null && org != null)
            {
                var orgApps = await org.GetChildrenAsync <Organization, Application>(dbCtx);

                foreach (var app in orgApps)
                {
                    if (!appCollector.Exists(a => a.Uuid == app.Uuid))
                    {
                        appCollector.Add(app);
                    }
                }
            }

            var outApps = new List <Application>();

            foreach (var a in appCollector)
            {
                if (
                    !a.IsApi &&                            //discard apis, they are not 'user apps'
                    (
                        a.IsDefault ||                     //always return the dashboard
                        (a.IsCommon && !a.RequiresAuth) || //and the public apps with no auth
                        (org != null && (await org.GetUserAppAccessCredentialsAsync(dbCtx, user, a)).CanUseApp)
                    )
                    )
                {
                    outApps.Add(a);
                }
            }

            //TODO - more ordering - stuff like special apps that are not public, but assigned to orgs directly, etc. Also, maybe some differentiation between freely accessible apps and the apps with auth.

            //stuff like home & dashboard always at the beginning and such...
            return(outApps.OrderByDescending(a => a.IsHome).ThenByDescending(a => a.IsDefault).ThenBy(a => a.Name));;
        }