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

Readjustement of the introduction image and first version of casbin authorization mechanism #201

Merged
merged 20 commits into from
Nov 28, 2022

Conversation

Ghita2002
Copy link
Contributor

No description provided.

@Ghita2002 Ghita2002 requested a review from a team as a code owner November 1, 2022 13:35
@coveralls
Copy link

coveralls commented Nov 1, 2022

Pull Request Test Coverage Report for Build 3517567483

Warning: This coverage report may be inaccurate.

This pull request's base commit is no longer the HEAD commit of its target branch. This means it includes changes from outside the original pull request, including, potentially, unrelated coverage changes.

Details

  • 0 of 0 changed or added relevant lines in 0 files are covered.
  • 4 unchanged lines in 1 file lost coverage.
  • Overall coverage decreased (-7.2%) to 56.825%

Files with Coverage Reduction New Missed Lines %
services/dkg/pedersen/controller/action.go 4 48.97%
Totals Coverage Status
Change from base Build 3320078377: -7.2%
Covered Lines: 3272
Relevant Lines: 5758

💛 - Coveralls

@Ghita2002 Ghita2002 marked this pull request as draft November 1, 2022 13:44
@Ghita2002 Ghita2002 requested a review from nkcr November 1, 2022 13:44
@Ghita2002 Ghita2002 self-assigned this Nov 1, 2022
@Ghita2002 Ghita2002 marked this pull request as ready for review November 2, 2022 14:09
Copy link
Contributor

@pierluca pierluca left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice progress !
I just made a quick pass on the codebase, this is not a full in-depth review and I'll let @emduc and @nkcr give their own feedback.

There are a couple of remarks that apply throughout. Let me know if you have questions 😄

Comment on lines 179 to 188
// function isAuthorized(roles: string[], req: express.Request): boolean {
// return true;
// if (!req.session || !req.session.userid) {
// return false;
// }

const { role } = req.session;
// const { role } = req.session;

return roles.includes(role as string);
}
// return roles.includes(role as string);
// }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

feel free to delete code that is not relevant anymore. Commented out code is generally frowned upon 😄

usersDB.getRange(opts).map(({ key, value }) => ({ id: '0', sciper: key, role: value }))
);
res.json(users);
app.get('/api/user_rights', async (req, res) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are you sure that express supports async handlers natively?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's my mistake, I forgot to delete it. I was just trying some things while testing.

);
res.json(users);
app.get('/api/user_rights', async (req, res) => {
e.then((enforcer) =>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if you have an async function, you don't need to do .then and .catch, you can just use await where you would use a then, and try/catch instead of .catch.

see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await

Comment on lines 212 to 213
.catch((erreur) => console.log('error', erreur))
).catch((erreur1) => console.log('error', erreur1));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

avoid french words in the codebase 😁
also, you can reuse the same variable name, as it's scoped just to that function.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the view, it is fixed.

@Ghita2002 Ghita2002 changed the title Readjustement of the introduction image Readjustement of the introduction image and first version of casbin authorization mechanism Nov 7, 2022
- Updates the initialisation of Casbin so that we start only when the enforcer is ready
- Uses enforceSync, which makes the checking mechanism was easier
- Adds a utility function isAuthorized
Comment on lines +35 to +37
Promise.all([enforcerLoading])
.then((res) => {
[enf] = res;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you need a Promise.all if you're looking at just one promise resolving?
Why can't you do the following ?

Suggested change
Promise.all([enforcerLoading])
.then((res) => {
[enf] = res;
enforcerLoading
.then((enf) => {

I may be missing something here, so do let me know if I'm wrong 😁

let enf: Enforcer;

const enforcerLoading = newEnforcer('model.conf', 'policy.csv');
const e = newEnforcer('model.conf', 'policy.csv');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this looks unused

@@ -26,6 +26,27 @@ const app = express();

app.use(morgan('tiny'));

let enf: Enforcer;

const enforcerLoading = newEnforcer('model.conf', 'policy.csv');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you need this to be in the global scope, or could you put it (along with the promise resolution below) in an initialization function ?

}

next();
e.then((enforcer) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there are a reason not to use isAuthorized here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just forget it. It is fixed !

Comment on lines 478 to 489
e.then((enforcer) => {
enforcer
.enforce(req.session.userid, 'election', 'create')
.then((isOk) => {
if (isOk) {
next();
} else {
res.status(400).send('Unauthorized - only admins and operators allowed');
}
})
.catch((error) => console.log('error', error));
}).catch((error1) => console.log('error', error1));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As per our discussion today, this should work:

Suggested change
e.then((enforcer) => {
enforcer
.enforce(req.session.userid, 'election', 'create')
.then((isOk) => {
if (isOk) {
next();
} else {
res.status(400).send('Unauthorized - only admins and operators allowed');
}
})
.catch((error) => console.log('error', error));
}).catch((error1) => console.log('error', error1));
e.then((enforcer) => {
return enforcer
.enforce(req.session.userid, 'election', 'create')
.then((isOk) => {
if (isOk) {
next();
} else {
res.status(400).send('Unauthorized - only admins and operators allowed');
}
})
}).catch((error) => console.log('error', error));

No need to do a double-catch 😁
And since you're directly returning a value, you could also do this (notice the absence of {} and return):

Suggested change
e.then((enforcer) => {
enforcer
.enforce(req.session.userid, 'election', 'create')
.then((isOk) => {
if (isOk) {
next();
} else {
res.status(400).send('Unauthorized - only admins and operators allowed');
}
})
.catch((error) => console.log('error', error));
}).catch((error1) => console.log('error', error1));
e.then((enforcer) =>
enforcer
.enforce(req.session.userid, 'election', 'create')
.then((isOk) => {
if (isOk) {
next();
} else {
res.status(400).send('Unauthorized - only admins and operators allowed');
}
})).catch((error) => console.log('error', error));

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I modify that using the proposition of Noemien.

Comment on lines 580 to 583
// const port = process.env.PORT || 5000;
// app.listen(port);

console.log(`App is listening on port ${port}`);
// console.log(`App is listening on port ${port}`);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

feel free to delete code, no need to comment it out 😁

@nkcr nkcr requested a review from pierluca November 21, 2022 08:11
@@ -177,7 +181,7 @@ const LeftSideNavBar = ({ authCtx, t }) => (
className={'text-black text-lg hover:text-indigo-700'}>
{t('navBarStatus')}
</NavLink>
{authCtx.role === 'admin' && authCtx.isLogged && (
{authCtx.isLogged && authCtx.authorization.get('roles').indexOf('list') > -1 && (
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you could define and use the constants on the front-end as well.

Comment on lines 154 to 155
authCtx.authorization.has('election') &&
authCtx.authorization.get('election').indexOf('create') > -1 && (
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and here

Comment on lines 92 to 93
authCtx.authorization.has('election') &&
authCtx.authorization.get('election').indexOf('create') > -1 && (
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and here

@@ -67,7 +67,7 @@ const MobileMenu = ({ authCtx, handleLogout, fctx, t }) => (
</Popover.Button>
</NavLink>
}
{authCtx.isLogged && (authCtx.role === 'admin' || authCtx.role === 'operator') && (
{authCtx.isLogged && authCtx.authorization.get('roles').indexOf('add') > -1 && (
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and here

Comment on lines 191 to 200
for (let i = 0; i < list.length; i += 1) {
for (let j = 1; j < list[0].length; j += +2) {
console.log(list[i][j]);
if (m.has(list[i][j])) {
m.get(list[i][j])?.push(list[i][j + 1]);
} else {
m.set(list[i][j], [list[i][j + 1]]);
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this kind of complex operations probably deserves its own function, that you can write a short test for.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

outside the scope of this PR: would it make sense to memoize this for performance reasons?

for (let j = 1; j < list[0].length; j += +2) {
console.log(list[i][j]);
if (m.has(list[i][j])) {
m.get(list[i][j])?.push(list[i][j + 1]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you really need the ? if you already have called m.has ?
conversely, why not just do m.get and do the push if the result is not null ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even if I check m.has I still have the error (Object is possibly 'undefined') This why I used ?. However it can be a good way to test the result of m.get. I was just wondering how it is better than using ?

Comment on lines 153 to 155
{authCtx.isLogged &&
authCtx.authorization.has('election') &&
authCtx.authorization.get('election').indexOf('create') > -1 && (
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking that here (and elsewhere) you're mixing up the details of the implementation (authorization is a map, permissions an array) and the actual concern (verifying access).

Could you add a helper function that will deal with these details ?
So that here (and elsewhere) you can just call something like hasAuthorization(authCtx, ROLES, LIST) ?
This will make it easier to refactor (if needed, in the future) the actual implementation details.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this review. It is quite a good idea. I implement it !

@Ghita2002 Ghita2002 requested a review from pierluca November 23, 2022 16:39
@@ -154,49 +185,58 @@ app.post('/api/logout', (req, res) => {
// As the user is logged on the app via this express but must also be logged in
// the react. This endpoint serves to send to the client (actually to react)
// the information of the current user.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment is for the statement bellow, not the setMapAuthorization function, which must also have its own comment describing what it does.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function is hard to understand without any explanation. You should at least describe what contains list: string[][]).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added some comments and while doing it I noticed that the iteration on j isn't necessary.

function setMapAuthorization(list: string[][]) {
const m = new Map<String, Array<String>>();
for (let i = 0; i < list.length; i += 1) {
for (let j = 1; j < list[0].length; j += +2) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you should explain (1) what is contained in list[0], and (2) why you are doing += 2.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I delete that.

const m = new Map<String, Array<String>>();
for (let i = 0; i < list.length; i += 1) {
for (let j = 1; j < list[0].length; j += +2) {
console.log(list[i][j]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you probably used it for debugging. To be removed.

app.get('/api/personal_info', (req, res) => {
let m = new Map<String, Array<String>>();
enf.getFilteredPolicy(0, String(req.session.userid)).then((list) => {
m = setMapAuthorization(list);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the user is not logged you can avoid calling setMapAuthorization and return an empty map.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed !


const MemoryStore = createMemoryStore(session);

const ROLES = 'roles';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is way too generic. You should prefix the variable name to indicate how the variable is used:

SUBJECT_ROLES = 'roles';
SUBJECT_PROXIES = 'proxies';
...
ACTION_LIST = 'list';
ACTION_REMOVE = 'remove';
...

@@ -24,6 +24,17 @@ import { Popover, Transition } from '@headlessui/react';
import { LoginIcon, LogoutIcon, MenuIcon, XIcon } from '@heroicons/react/outline';
import { PlusIcon } from '@heroicons/react/solid';

const ELECTION = 'election';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should prefix the variable name:

SUBJECT_ELECTION = ...
...
ACTION_CREATE = ...


function hasAuthorization(authCtx, subject: string, action: string): boolean {
return (
authCtx.authorization.has(subject) && authCtx.authorization.get(subject).indexOf(action) > -1
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
authCtx.authorization.has(subject) && authCtx.authorization.get(subject).indexOf(action) > -1
authCtx.authorization.has(subject) && authCtx.authorization.get(subject).indexOf(action) !== -1

@Ghita2002 Ghita2002 requested a review from nkcr November 28, 2022 13:48
Copy link
Contributor

@pierluca pierluca left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not absolutely perfect, but sooooo much better, great job ! 👍

I've some minor remarks, but at this point it looks nearly mergeable to me :)

// list[0] contains the policies so list[i][0] is the sciper
// list[i][1] is the object and list[i][2] is the action
function setMapAuthorization(list: string[][]) {
const m = new Map<String, Array<String>>();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you want to use String the object or just string the plain data type?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When searching which one use, I found that it is better to avoid bugs to use string

// an object to the action authorized
// list[0] contains the policies so list[i][0] is the sciper
// list[i][1] is the object and list[i][2] is the action
function setMapAuthorization(list: string[][]) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in typescript it's good to explicitly indicate the return type.

Suggested change
function setMapAuthorization(list: string[][]) {
function setMapAuthorization(list: string[][]): Map<string, Array<string>> {

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done !

@@ -207,7 +215,7 @@ app.get('/api/personal_info', (req, res) => {
firstname: req.session.firstname,
role: req.session.role,
islogged: true,
authorization: Object.fromEntries(m),
authorization: Object.fromEntries(setMapAuthorization(list)),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

much cleaner :)

console.log(m);
return m;
}

// As the user is logged on the app via this express but must also be logged in
// the react. This endpoint serves to send to the client (actually to react)
// the information of the current user.
app.get('/api/personal_info', (req, res) => {
const m = new Map<String, Array<String>>();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you still need this variable?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I decide to delete it

function setMapAuthorization(list: string[][]) {
const m = new Map<String, Array<String>>();
for (let i = 0; i < list.length; i += 1) {
if (m.has(list[i][1])) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment is already a great improvement. 👍

Now, the next step is self-explanatory code.. ;)

You could do:

const [subject, action] = list[i][1], list[i][2];
if (m.has(subject)) {
...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the review ! Done

const m = new Map<String, Array<String>>();
for (let i = 0; i < list.length; i += 1) {
if (m.has(list[i][1])) {
m.get(list[i][1])?.push(list[i][2]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I understand the ?. Is it to prevent adding multiple times the same action ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's necessary, however I check that m.has(list[i][1], to make sure that m.get(list[i][1] is not undefined.

firstname: '',
role: '',
islogged: false,
authorization: Object.fromEntries(m),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can just use authorization: new Map<String, Array<String>>(),
or even just authorization: {},

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed !

@sonarqubecloud
Copy link

Kudos, SonarCloud Quality Gate passed!    Quality Gate passed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 9 Code Smells

0.0% 0.0% Coverage
0.0% 0.0% Duplication

@Ghita2002 Ghita2002 requested review from nkcr and pierluca November 28, 2022 16:20
Copy link
Contributor

@nkcr nkcr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good to go 🚀

@Ghita2002 Ghita2002 merged commit 0f99cbf into main Nov 28, 2022
@Ghita2002 Ghita2002 deleted the d-voting_frontend_ghita branch November 28, 2022 18:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants