Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

move to production #3917

Merged
merged 12 commits into from
Nov 25, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion k8s/analytics/values-prod.yaml
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@ images:
celeryWorker: eu.gcr.io/airqo-250220/airqo-analytics-celery-worker
reportJob: eu.gcr.io/airqo-250220/airqo-analytics-report-job
devicesSummaryJob: eu.gcr.io/airqo-250220/airqo-analytics-devices-summary-job
tag: prod-ffa7c76b-1732557102
tag: prod-8a436c69-1732561912
api:
name: airqo-analytics-api
label: analytics-api
2 changes: 1 addition & 1 deletion k8s/auth-service/values-prod.yaml
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@ app:
replicaCount: 3
image:
repository: eu.gcr.io/airqo-250220/airqo-auth-api
tag: prod-ffa7c76b-1732557102
tag: prod-8a436c69-1732561912
nameOverride: ''
fullnameOverride: ''
podAnnotations: {}
2 changes: 1 addition & 1 deletion k8s/auth-service/values-stage.yaml
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@ app:
replicaCount: 2
image:
repository: eu.gcr.io/airqo-250220/airqo-stage-auth-api
tag: stage-bd23841e-1732556820
tag: stage-75cbc431-1732565904
nameOverride: ''
fullnameOverride: ''
podAnnotations: {}
2 changes: 1 addition & 1 deletion k8s/device-registry/values-prod.yaml
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@ app:
replicaCount: 3
image:
repository: eu.gcr.io/airqo-250220/airqo-device-registry-api
tag: prod-ffa7c76b-1732557102
tag: prod-8a436c69-1732561912
nameOverride: ''
fullnameOverride: ''
podAnnotations: {}
2 changes: 1 addition & 1 deletion k8s/exceedance/values-prod-airqo.yaml
Original file line number Diff line number Diff line change
@@ -4,6 +4,6 @@ app:
configmap: env-exceedance-production
image:
repository: eu.gcr.io/airqo-250220/airqo-exceedance-job
tag: prod-ffa7c76b-1732557102
tag: prod-8a436c69-1732561912
nameOverride: ''
fullnameOverride: ''
2 changes: 1 addition & 1 deletion k8s/exceedance/values-prod-kcca.yaml
Original file line number Diff line number Diff line change
@@ -4,6 +4,6 @@ app:
configmap: env-exceedance-production
image:
repository: eu.gcr.io/airqo-250220/kcca-exceedance-job
tag: prod-ffa7c76b-1732557102
tag: prod-8a436c69-1732561912
nameOverride: ''
fullnameOverride: ''
2 changes: 1 addition & 1 deletion k8s/predict/values-prod.yaml
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@ images:
predictJob: eu.gcr.io/airqo-250220/airqo-predict-job
trainJob: eu.gcr.io/airqo-250220/airqo-train-job
predictPlaces: eu.gcr.io/airqo-250220/airqo-predict-places-air-quality
tag: prod-ffa7c76b-1732557102
tag: prod-8a436c69-1732561912
api:
name: airqo-prediction-api
label: prediction-api
201 changes: 66 additions & 135 deletions src/auth-service/models/User.js
Original file line number Diff line number Diff line change
@@ -17,7 +17,6 @@ const logger = require("log4js").getLogger(
const validUserTypes = ["user", "guest"];
const { HttpError } = require("@utils/errors");
const mailer = require("@utils/mailer");
const ORGANISATIONS_LIMIT = 6;

function oneMonthFromNow() {
var d = new Date();
@@ -248,27 +247,14 @@ UserSchema.path("group_roles.userType").validate(function (value) {
return validUserTypes.includes(value);
}, "Invalid userType value");

UserSchema.pre("save", async function (next) {
// Password hashing
UserSchema.pre("save", function (next) {
if (this.isModified("password")) {
this.password = bcrypt.hashSync(this.password, saltRounds);
}

// Validate contact information
if (!this.email && !this.phoneNumber) {
return next(new Error("Phone number or email is required!"));
}

// Profile picture validation
if (this.profilePicture && !validateProfilePicture(this.profilePicture)) {
return next(
new HttpError("Bad Request Error", httpStatus.BAD_REQUEST, {
message: "Invalid profile picture URL",
})
);
}

// Network roles handling
if (!this.network_roles || this.network_roles.length === 0) {
if (
!constants ||
@@ -295,7 +281,6 @@ UserSchema.pre("save", async function (next) {
];
}

// Group roles handling
if (!this.group_roles || this.group_roles.length === 0) {
if (
!constants ||
@@ -307,7 +292,7 @@ UserSchema.pre("save", async function (next) {
httpStatus.INTERNAL_SERVER_ERROR,
{
message:
"Contact [email protected] -- unable to retrieve the default Group or Role",
"Contact [email protected] -- unable to retrieve the default Group or Role to which the User will belong",
}
);
}
@@ -322,119 +307,23 @@ UserSchema.pre("save", async function (next) {
];
}

// Permissions handling
if (this.permissions && this.permissions.length > 0) {
// Additional permissions validation can be added here if needed
this.permissions = [...new Set(this.permissions)]; // Ensure unique permissions
if (!this.verified) {
this.verified = false;
}

// Ensure default values
this.verified = this.verified ?? false;
this.analyticsVersion = this.analyticsVersion ?? 2;
if (!this.analyticsVersion) {
this.analyticsVersion = 2;
}

return next();
});

UserSchema.pre(
["updateOne", "findOneAndUpdate", "updateMany", "update", "save"],
function (next) {
if (this.getUpdate) {
const updates = this.getUpdate();
const fieldsToValidate = [
"_id",
"firstName",
"lastName",
"userName",
"email",
"organization",
"long_organization",
"privilege",
"country",
"profilePicture",
"phoneNumber",
"createdAt",
"updatedAt",
"rateLimit",
"lastLogin",
"iat",
];

fieldsToValidate.forEach((field) => {
// Check for empty/null/undefined values
const value = updates[field] || (updates.$set && updates.$set[field]);

if (value === undefined || value === null || value === "") {
return next(
new HttpError("Validation Error", httpStatus.BAD_REQUEST, {
message: `${field} cannot be empty, null, or undefined`,
})
);
}
});
if (updates) {
// Prevent modification of certain immutable fields
const immutableFields = ["firebase_uid", "email", "createdAt", "_id"];

immutableFields.forEach((field) => {
if (updates[field]) delete updates[field];

if (updates.$set && updates.$set[field]) {
return next(
new HttpError(
"Modification Not Allowed",
httpStatus.BAD_REQUEST,
{ message: `Cannot modify ${field} after creation` }
)
);
}

if (updates.$set) delete updates.$set[field];
if (updates.$push) delete updates.$push[field];
});

// Validate network roles and group roles limits
if (
updates.network_roles &&
updates.network_roles.length > ORGANISATIONS_LIMIT
) {
return next(
new HttpError("Validation Error", httpStatus.BAD_REQUEST, {
message: `Maximum ${ORGANISATIONS_LIMIT} network roles allowed`,
})
);
}

if (
updates.group_roles &&
updates.group_roles.length > ORGANISATIONS_LIMIT
) {
return next(
new HttpError("Validation Error", httpStatus.BAD_REQUEST, {
message: `Maximum ${ORGANISATIONS_LIMIT} group roles allowed`,
})
);
}

// Ensure password is hashed if modified
if (updates.password) {
updates.password = bcrypt.hashSync(updates.password, saltRounds);
}
}
}

// Additional checks for new documents
if (this.isNew) {
const requiredFields = ["firstName", "lastName", "email"];
requiredFields.forEach((field) => {
if (this.isModified(field) && !this[field]) {
return next(new Error(`${field} is required`));
}
});
}

next();
UserSchema.pre("update", function (next) {
if (this.isModified("password")) {
this.password = bcrypt.hashSync(this.password, saltRounds);
}
);
return next();
});

UserSchema.index({ email: 1 }, { unique: true });
UserSchema.index({ userName: 1 }, { unique: true });
@@ -813,18 +702,61 @@ UserSchema.statics = {
async modify({ filter = {}, update = {} } = {}, next) {
try {
logText("the user modification function........");
const options = { new: true };
let options = { new: true };
const fieldNames = Object.keys(update);
const fieldsString = fieldNames.join(" ");
let modifiedUpdate = update;
modifiedUpdate["$addToSet"] = {};

if (update.password) {
modifiedUpdate.password = bcrypt.hashSync(update.password, saltRounds);
}

if (modifiedUpdate.profilePicture) {
if (!validateProfilePicture(modifiedUpdate.profilePicture)) {
next(
new HttpError("Bad Request Error", httpStatus.BAD_REQUEST, {
message: "Invalid profile picture URL",
})
);
}
}

if (modifiedUpdate.network_roles) {
if (isEmpty(modifiedUpdate.network_roles.network)) {
delete modifiedUpdate.network_roles;
} else {
modifiedUpdate["$addToSet"] = {
network_roles: { $each: modifiedUpdate.network_roles },
};
delete modifiedUpdate.network_roles;
}
}

if (modifiedUpdate.group_roles) {
if (isEmpty(modifiedUpdate.group_roles.group)) {
delete modifiedUpdate.group_roles;
} else {
modifiedUpdate["$addToSet"] = {
group_roles: { $each: modifiedUpdate.group_roles },
};
delete modifiedUpdate.group_roles;
}
}

if (modifiedUpdate.permissions) {
modifiedUpdate["$addToSet"]["permissions"] = {};
modifiedUpdate["$addToSet"]["permissions"]["$each"] =
modifiedUpdate.permissions;
delete modifiedUpdate["permissions"];
}

// Find and update user
const updatedUser = await this.findOneAndUpdate(
filter,
update,
modifiedUpdate,
options
).select(fieldsString);

// Handle update result
if (!isEmpty(updatedUser)) {
const { _id, ...userData } = updatedUser._doc;
return {
@@ -833,17 +765,16 @@ UserSchema.statics = {
data: userData,
status: httpStatus.OK,
};
} else if (isEmpty(updatedUser)) {
next(
new HttpError("Bad Request Error", httpStatus.BAD_REQUEST, {
message: "user does not exist, please crosscheck",
})
);
}

// User not found
return next(
new HttpError("Bad Request Error", httpStatus.BAD_REQUEST, {
message: "user does not exist, please crosscheck",
})
);
} catch (error) {
logger.error(`🐛🐛 Internal Server Error -- ${error.message}`);
return next(
next(
new HttpError(
"Internal Server Error",
httpStatus.INTERNAL_SERVER_ERROR,
1,006 changes: 1,006 additions & 0 deletions src/auth-service/models/UserNew.js

Large diffs are not rendered by default.