Как создать схему схемы MongoDB при работе с одной учетной записью пользователя и несколькими пользователями (учетная запись организации с ролями) - PullRequest
0 голосов
/ 21 декабря 2018

Я работаю над проектом API Nodejs Express, использующим mongoDB с mongoose, и хотел бы получить несколько советов о передовых практиках и о создании эффективного дизайна схемы из сообщества.

Приложение работает с двумя типами:учетные записи пользователей

Тип учетной записи:

  • Одиночная (по умолчанию)
  • Организация (можно переключиться из настроек)

Примечание : В учетной записи организации будет администратор (владелец) и другой приглашенный пользователь, и каждому пользователю будет назначен уровень разрешений / уровень доступа. Один пользователь всегда будет связан только с одной учетной записью, т.е. его нельзя пригласить.снова к другой учетной записи или создать новую учетную запись, если он уже является частью существующей учетной записи.Кроме того, адрес выставления счета и доставки зависит от учетной записи, а не от пользователя, в случае учетной записи организации (адрес переключения пользователя на учетную запись организации будет являться адресом учетной записи организации)

Я завершил часть проверки подлинностис помощью passport.js JWT и локальной стратегии

я попытался разработать подход, аналогичный подходу RDBMS (раньше я был RDBMSпарень) и не удалось

enter image description here

Модели и схемы

const userSchema = new Schema({
    first_name: String,
    last_name: String,
    email: String,
    phone: String,
    avatar: String,
    password: String,
    active: Boolean
});

const User = mongoose.model('user', userSchema);

const accountSchema =  mongoose.Schema({
    account_type: { type: String, enum: ['single', 'organization'], default: 'single' },
    organization: { type: Schema.Types.ObjectId, ref: 'organization', required: false },
    billing_address: String,
    shipping_address: String,

});

const Account = mongoose.model('account', accountSchema);

const accountUserRoleSchema =  mongoose.Schema({
    user :  { type: Schema.Types.ObjectId, ref: 'user', },
    role: { type: String, enum: ['admin', 'user'], default: 'user' },
    account: { type: Schema.Types.ObjectId, ref: 'account', required: true  }
});

const AccountUserRole = mongoose.model('accountUserRole', accountUserRoleSchema);


const permissionSchema =  mongoose.Schema({
    user :  { type: Schema.Types.ObjectId, ref: 'user', required: true },
    type: {  type: Schema.Types.ObjectId, ref: 'permissionType', required: true  },
    read: { type: Boolean, default: false, required: true  },
    write: { type: Boolean, default: false, required: true },
    delete: { type: Boolean, default: false, required: true },
    accountUser : {  type: Schema.Types.ObjectId, ref: 'account',required: true }

});

const Permission = mongoose.model('permission', permissionSchema);


const permissionTypeSchema =  mongoose.Schema({
    name :  { type: String, required: true   }

});

const PermissionType = mongoose.model('permissionType', permissionTypeSchema); 


const organizationSchema =  mongoose.Schema({
    account :  { type: Schema.Types.ObjectId, ref: 'account', },
    name: {  type: String, required: true },
    logo: { type: String, required: true  }
});


const Organization = mongoose.model('organization', organizationSchema);

Сейчас я разрабатываю часть авторизации, гдепользователю необходимо ограничить доступ к ресурсу, проверив разрешение, которое ему назначено.

Решение, которое я нашел, заключалось в разработке промежуточного программного обеспечения авторизации, которое запускается после промежуточного программного обеспечения аутентификации, которое проверяет назначенные права доступа.

Но проблема возникла, когда я пытался получить доступ к данным учетной записи на основе пользователя, вошедшего в систему в данный момент, как я будудолжен искать документ на основе ссылки на objectId.И я мог бы понять, что это может произойти, если я продолжу свой текущий дизайн. Это работает нормально, но поиск документа с использованием ссылки на objectId кажется не очень хорошей идеей

Промежуточное программное обеспечение авторизации

module.exports = {

    checkAccess :  (permission_type,action) => {

        return  async (req, res, next) => {

            // check if the user object is in the request after verifying jwt
            if(req.user){

                // find the accountUserRole with the user data from the req after passort jwt auth
                const accountUser = await AccountUserRole.findOne({ user :new ObjectId( req.user._id) }).populate('account');
                if(accountUser)
                {
                    // find  the account  and check the type 

                    if(accountUser.account)
                    {   
                        if(accountUser.account.type === 'single')
                        {   
                            // if account  is single grant access
                            return next();
                        }
                        else if(accountUser.account.type === 'organization'){


                             // find the user permission 

                             // check permission with permission type and see if action is true 

                             // if true move to next middileware else throw  access denied error  


                        }
                    }

                }

            }
        }


    }


}

Я решил отказаться от своей текущей схемы, так как понимаю, что форсирование подхода RDBMS к NoSQL - плохая идея.

В отличие от реляционных баз данных, с MongoDB лучший дизайн схемы во многом зависит от того, как высобираемся получить доступ к данным.Для чего вы будете использовать данные учетной записи и как вы будете получать к ней доступ

Мои новые переработанные схемы и модели

const userSchema = new Schema({
    first_name: String,
    last_name: String,
    email: String,
    phone: String,
    avatar: String,
    password: String,
    active: Boolean
    account :  { type: Schema.Types.ObjectId, ref: 'account', },
    role: { type: String, enum: ['admin', 'user'], default: 'user' },
    permssion: [
        {
            type: {  type: Schema.Types.ObjectId, ref: 'permissionType', required: true  },
            read: { type: Boolean, default: false, required: true  },
            write: { type: Boolean, default: false, required: true },
            delete: { type: Boolean, default: false, required: true },
        }
    ]

});

const User = mongoose.model('user', userSchema);

const accountSchema =  mongoose.Schema({
    account_type: { type: String, enum: ['single', 'organization'], default: 'single' },
    organization: {  
            name: {  type: String, required: true },
            logo: { type: String, required: true  }
         },
    billing_address: String,
    shipping_address: String,

});


const Account = mongoose.model('account', accountSchema);


const permissionTypeSchema =  mongoose.Schema({
    name :  { type: String, required: true   }

});

const PermissionType = mongoose.model('permissionType', permissionTypeSchema);

Тем не менее яНе уверен, что это правильный способ, пожалуйста, помогите мне с вашими предложениями.

Ответы [ 2 ]

0 голосов
/ 21 декабря 2018

Я бы предложил:

1 - Определите свои уровни разрешений, например: если пользователю назначена определенная роль / уровень доступа, к каким функциям / параметрам он может получить доступ.

2 - Уровни разрешений должны распознаваться по номеру (1 = Администратор, 2 = Пользователь) и т. Д., И этот ключ должен индексироваться в MongoDB (Вы также можете использовать ObjectID и полагаться на него).

3 - Ваш пользовательобъект / схема должны иметь только ключ доступа с типом Number в Mongoose - для этого не нужно создавать отдельную схему.

 const userSchema = new Schema({
    first_name: String,
    last_name: String,
    email: String,
    phone: String,
    avatar: String,
    password: String,
    active: Boolean
    account :  { type: Schema.Types.ObjectId, ref: 'account', },
    permssion: {type: Number, required: true, default: 2} // Default's User

});

При таком подходе вы можете изменить свойПроверка подлинности промежуточного программного обеспечения, чтобы просто проверить, идентифицирован ли БД уровень разрешений, отправленный клиентом, и если это так, предоставьте пользователю доступ, иначе выведите ошибку «Отказано в доступе».

Если вы хотите, вы можете добавить другое поле с разрешениемвведите и верните имя разрешения, но я думаю, что вы должны обработать его на клиенте, а не на сервере / be.

Я частично понял требованиеments (плохо читает слишком много слов), поэтому я оставил все без изменений, дайте мне знать.

0 голосов
/ 21 декабря 2018

вы можете объединить пользователя и схему учетной записи пользователя:

добавил еще несколько полей, которые вам пригодятся.

const userSchema = new Schema({
    first_name: { type: String,default:'',required:true},
    last_name: { type: String,default:'',required:true},
    email:  { type: String,unique:true,required:true,index: true},
    email_verified :{type: Boolean,default:false},
    email_verify_token:{type: String,default:null},
    phone:  { type: String,default:''},
    phone_verified :{type: Boolean,default:false},
    phone_otp_number:{type:Number,default:null},
    phone_otp_expired_at:{ type: Date,default:null},
    avatar:  { type: String,default:''},
    password: { type: String,required:true},
    password_reset_token:{type: String,default:null},
    reset_token_expired_at: { type: Date,default:null},
    active: { type: Boolean,default:true}
    account_type: { type: String, enum: ['single', 'organization'], default: 'single' },
    organization: {type:Schema.Types.Mixed,default:{}},
    billing_address: { type: String,default:''}
    shipping_address: { type: String,default:''}
    role: { type: String, enum: ['admin', 'user'], default: 'user' },
    permission: [
        {
            type: {  type: Schema.Types.ObjectId, ref: 'permissionType', required: true  },
            read: { type: Boolean, default: false, required: true  },
            write: { type: Boolean, default: false, required: true },
            delete: { type: Boolean, default: false, required: true },
        }
    ],
   created_at: { type: Date, default: Date.now },
   updated_at: { type: Date, default: Date.now }
});

в вашем промежуточном программном обеспечении:

module.exports = {

  checkAccess :  (permission_type,action) => {

    return  async (req, res, next) => {

        // check if the user object is in the request after verifying jwt
         if(req.user){
              if(req.user.account_type === 'single')
                    {   
                        // if account  is single grant access
                        return next();
                    }
                    else{


                         // find the user permission 

                         // check permission with permission type and see if action is true 

                         // if true move to next middileware else throw  access denied error  


                    }
         }
       }
   }
};
...