Ballroom-Platform / ballroom-backend

Apache License 2.0
2 stars 8 forks source link

Implementing a permission model for contests and challenges. #76

Closed ManujaDewmina closed 1 year ago

ManujaDewmina commented 1 year ago

Admins could be able to give permissions to other admins to view and edit their contests and challenges. Only Owner can delete their contests and challenges.

ManujaDewmina commented 1 year ago

Add this entity in entity-model

// Many-to-many relations between user and Contest
type contestAccess record {|
    readonly string id;
    Contest contest;
    User user;
    string accessType;
|};

New added Resource functions for contest model

  1. To get owner owned contests

    resource function get contests/[string status]/owned/[string userId]() returns data_model:Contest[]|http:InternalServerError|http:NotFound {
    
        data_model:Contest[]|persist:Error contests = getOwnerContests(self.db, userId, status);
        if contests is persist:Error {
            log:printError("Error while retrieving contests", 'error = contests);
            return <http:InternalServerError>{
                body: {
                    message: string `Error while retrieving contests`
                }
            };
        } else {
            return contests;
        }
    }
    
    function getOwnerContests(entities:Client db, string userId, string status) returns data_model:Contest[]|persist:Error {
    
    stream<entities:Contest, persist:Error?> contestStream = db->/contests;
    
    entities:Contest[]|persist:Error contests = from var contest in contestStream
        select contest;
    
    if contests is persist:Error {
        log:printError("Error while reading contests data", 'error = contests);
        return contests;
    } else {
        return from var contest in contests
            where compareTime(contest.startTime, contest.endTime) == status && contest.moderatorId == userId
            select toDataModelContest(contest);
    }
    }

2. To get shared contests with user by other admins

type SharedContest record {| string userId; string accessType; record {| string id; string title; string description; time:Civil startTime; time:Civil endTime; string imageUrl; string moderatorId; |} contest; |};

resource function get contests/[string status]/shared/[string userId]() returns SharedContest[]|http:InternalServerError|http:NotFound {

    SharedContest[]|persist:Error contests = getSharedContests(self.db, userId, status);
    if contests is persist:Error {
        log:printError("Error while retrieving contests", 'error = contests);
        return <http:InternalServerError>{
            body: {
                message: string `Error while retrieving contests`
            }
        };
    } else {
        return contests;
    }
}

function getSharedContests(entities:Client db, string userId, string status) returns SharedContest[]|persist:Error {

stream<SharedContest, persist:Error?> sharedContestStream = db->/contestaccesses;

SharedContest[]|persist:Error sharedContests = from var sharedContest in sharedContestStream
    select sharedContest;

if sharedContests is persist:Error {
    log:printError("Error while reading contests data", 'error = sharedContests);
    return sharedContests;
} else {
    return from var sharedContest in sharedContests
        where compareTime(sharedContest.contest.startTime, sharedContest.contest.endTime) == status && sharedContest.userId == userId
        select sharedContest;
}

}


3. To get admin users who have access to a selected contest

type Payload record { string message; anydata data; };

type contestAccessAdmins record {| string contestId; string accessType; record {| string id; string username; string fullname; |} user; |};

resource function get contests/[string contestId]/access() returns Payload|http:InternalServerError|error { do { contestAccessAdmins[]|persist:Error result = getContestAdmins(self.db, contestId) ?: [];

        Payload responsePayload = {
            message: "Admin access table created",
            data: check result
        };
        return responsePayload;

    } on fail error e {
        log:printError("Error while creating admin access table", 'error = e);
        return http:INTERNAL_SERVER_ERROR;
    }
}

function getContestAdmins(entities:Client db, string contestId) returns contestAccessAdmins[]|persist:Error? {

stream<contestAccessAdmins, persist:Error?> contestAccessStream = db->/contestaccesses;

contestAccessAdmins[]|persist:Error contestAccesses = from var contestAccess in contestAccessStream
    select contestAccess;

if contestAccesses is persist:Error {
    log:printError("Error while reading contests data", 'error = contestAccesses);
    return contestAccesses;
} else {
    return from var contestAccess in contestAccesses
        where contestAccess.contestId == contestId
        select contestAccess;
}

}


4. To give access for contests

resource function post contests/[string contestId]/access/[string userId]/[string accessType]() returns string|http:BadRequest|http:InternalServerError { // Check for duplications. stream<entities:contestAccess, persist:Error?> contestAccesses = self.db->/contestaccesses;

    entities:contestAccess[]|persist:Error duplicates = from var contestAccess in contestAccesses
        where contestAccess.contestId == contestId && contestAccess.userId == userId
        select contestAccess;
    if duplicates is persist:Error {
        log:printError("Error while reading contestAccesss data", 'error = duplicates);
        return <http:InternalServerError>{
            body: {
                message: string `Error while adding admins to contest`
            }
        };
    }

    if duplicates.length() > 0 {
        return <http:BadRequest>{
            body: {
                message: string `Admin already added to contest`
            }
        };
    }

    string[]|persist:Error insertedIds = self.db->/contestaccesses.post([
        {
            id: uuid:createType4AsString(),
            contestId: contestId,
            userId: userId,
            accessType: accessType
        }
    ]);

    if insertedIds is persist:Error {
        log:printError("Error while adding admin to contest", 'error = insertedIds);
        return <http:InternalServerError>{
            body: {
                message: string `Error while adding challenge to contest`
            }
        };
    } else {
        return insertedIds[0];
    }
}
ManujaDewmina commented 1 year ago
  1. Delete given access for a contest
resource function delete contests/[string contestId]/access/[string userId]() returns http:InternalServerError|http:NotFound|http:Ok {
        stream<entities:contestAccess, persist:Error?> contestAccessStream = self.db->/contestaccesses;

        entities:contestAccess[]|persist:Error contestAccesses = from var contestAccess in contestAccessStream
            select contestAccess;

        if contestAccesses is persist:Error {
            log:printError("Error while reading contests data", 'error = contestAccesses);
            return <http:InternalServerError>{
                body: {
                    message: string `Error while retrieving users`
                }
            };
        } else {
            entities:contestAccess[] listResult = from var contestAccess in contestAccesses
                where contestAccess.contestId == contestId && contestAccess.userId == userId
                select contestAccess;

            if (listResult.length() == 0) {
                return <http:NotFound>{
                    body: {
                        message: string `User ${userId} hasn't access to contest ${contestId}`
                    }
                };
            }

            entities:contestAccess|persist:Error deletedContestAccess = self.db->/contestaccesses/[listResult[0].id].delete;

            if deletedContestAccess is persist:InvalidKeyError {
                return http:NOT_FOUND;
            } else if deletedContestAccess is persist:Error {
                log:printError("Error while deleting ", 'error = deletedContestAccess);
                return <http:InternalServerError>{
                    body: {
                        message: string `Error while deleting contest User ${userId} contest ${contestId} access`
                    }
                };
            } else {
                return http:OK;
            }
        }
    }