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

feat: add authorization to user/order route #231

Merged
merged 5 commits into from
Nov 11, 2019
Merged

Conversation

jannyHou
Copy link
Contributor

@jannyHou jannyHou commented Aug 8, 2019

issue: loopbackio/loopback-next#3695

implements loopback features in loopbackio/loopback-next#1205 and loopbackio/loopback-next#2900

The PR serves as a PoC and feature to verify implementation of @loopback/authorization in the shopping app.

items:

  • - Try apply the casbin based authorization in the shopping example
  • - Figure out the responsibilities of authentication, authorization, authorizeInterceptor, authorizor, fixtures(casbin)
  • - Extract the common layer for authentication and authorization
  • - implement one endpoint Users/{userId}/orders with casbin policies , connect with loopback feature Refactor authorization loopback-next#3529
  • - To be improved: casbin policies set fixed roles instead of checking the order's ownId on the fly. Solved by adding a voter.
  • - add acceptance and unit tests

async createOrder(
@param.path.string('userId') userId: string,
@requestBody() order: Order,
): Promise<Order> {
// should be moved to an authorizor
if (userId !== order.userId) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

or use voter to decide.

@jannyHou jannyHou changed the title [WIP, early feedback are welcomed] feat: try authorization [PoC] feat: try authorization Aug 9, 2019
@deepakrkris deepakrkris force-pushed the test/authorization branch 6 times, most recently from a4f9c7e to 1d6cce9 Compare November 5, 2019 02:18
@deepakrkris deepakrkris marked this pull request as ready for review November 5, 2019 02:20
@deepakrkris deepakrkris force-pushed the test/authorization branch 4 times, most recently from 7f27197 to 1bfb2ac Compare November 5, 2019 16:58
@deepakrkris deepakrkris changed the title [PoC] feat: try authorization feat: add authorization to user order model Nov 5, 2019
@deepakrkris deepakrkris force-pushed the test/authorization branch 2 times, most recently from 45d113e to 53604a9 Compare November 5, 2019 17:18
@deepakrkris deepakrkris changed the title feat: add authorization to user order model feat: add authorization to user/order route Nov 5, 2019
"pretest:ci": "npm run build",
"pretest": "npm run clean && npm run build && npm run docker:start",
Copy link
Contributor

Choose a reason for hiding this comment

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

Please remove && npm run docker:start

Copy link
Contributor

Choose a reason for hiding this comment

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

agreed

"bcryptjs": "2.4.3",
"casbin": "^3.0.3",
Copy link
Contributor

Choose a reason for hiding this comment

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

Please remove ^ from all versions.

Copy link
Contributor

Choose a reason for hiding this comment

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

agreed

}

async authorize(
// @jannyHou: make sure it's universal for authorizers
Copy link
Member

Choose a reason for hiding this comment

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

is it a reminder for something? Either way, I think it's the best to remove the @jannyHou. :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good catch...I think it's some notes from meeting, should be removed.

Copy link
Contributor

Choose a reason for hiding this comment

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

agreed, removing note

Copy link
Contributor Author

@jannyHou jannyHou left a comment

Choose a reason for hiding this comment

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

👍 Great progress! Mostly good, I left a few comments.

@@ -35,8 +42,11 @@ describe('UserOrderController acceptance tests', () => {
const userId = user.id;
const order = givenAOrder({userId: userId, orderId: '1'});

const token = await authenticateUser(user);
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 think we can just generate the token once, then use it in all the it tests.

Copy link
Contributor

Choose a reason for hiding this comment

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

actually every test iteration calls givenAUser() to create a new user for every test. so a token has to be created every time for the new user.

__dirname,
'../../fixtures/casbin/rbac_policy.csv',
);
return casbin.newEnforcer(conf, policy);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

good catch, and we can turn createEnforcer into a sync function since casbin.newEnforcer() is sync.

Copy link
Contributor

Choose a reason for hiding this comment

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

casbin.newEnforcer returns a promise, so to resolve it createEnforcer has to be async. The reason I removed the await is because of lint errors and await is added in a return from an async function internally.

@@ -54,7 +58,7 @@ export class JWTService implements TokenService {
);
}
const userInfoForToken = {
id: userProfile[securityId],
id: userProfile.id,
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 think it should still be userProfile[securityId]

Copy link
Contributor

Choose a reason for hiding this comment

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

userProfile.id and userProfile[securityId] have same values.

Copy link
Contributor

Choose a reason for hiding this comment

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

depends on what is returned by user service

  convertToUserProfile(user: User): UserProfile {
    // since first name and lastName are optional, no error is thrown if not provided
    let userName = '';
    if (user.firstName) userName = `${user.firstName}`;
    if (user.lastName)
      userName = user.firstName
        ? `${userName} ${user.lastName}`
        : `${user.lastName}`;
    return {[securityId]: user.id, name: userName, id: user.id};
  }

Copy link
Contributor Author

Choose a reason for hiding this comment

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

true in this case theoretically [securityId] always equals to id, but conceptually we should retrieve the value from a user profile's unique field, which is [securityId]

Copy link
Contributor

Choose a reason for hiding this comment

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

ok, I will correct this.

@deepakrkris deepakrkris force-pushed the test/authorization branch 2 times, most recently from 2432c69 to 33451b1 Compare November 8, 2019 17:25
Copy link
Contributor Author

@jannyHou jannyHou left a comment

Choose a reason for hiding this comment

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

@deepakrkris almost there, a few more comments.

@@ -82,6 +91,8 @@ export class UserOrderController {
},
},
})
@authenticate('jwt')
@authorize({resource: 'order', scopes: ['patch']})
Copy link
Contributor Author

Choose a reason for hiding this comment

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

ditto

Copy link
Contributor

Choose a reason for hiding this comment

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

agreed

@@ -66,6 +73,8 @@ export class UserOrderController {
},
},
})
@authenticate('jwt')
@authorize({resource: 'order', scopes: ['find']})
Copy link
Contributor Author

Choose a reason for hiding this comment

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

can we also add voters: [compareId] for patch and find methods?

Copy link
Contributor

Choose a reason for hiding this comment

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

agreed

@@ -54,7 +58,7 @@ export class JWTService implements TokenService {
);
}
const userInfoForToken = {
id: userProfile[securityId],
id: userProfile.id,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

true in this case theoretically [securityId] always equals to id, but conceptually we should retrieve the value from a user profile's unique field, which is [securityId]

@deepakrkris deepakrkris force-pushed the test/authorization branch 2 times, most recently from 0a27666 to f1136b7 Compare November 8, 2019 22:31
"@loopback/repository": "1.10.1",
"@loopback/rest": "1.16.6",
"@loopback/rest-explorer": "1.3.1",
"@loopback/security": "^0.1.6",
Copy link
Contributor

Choose a reason for hiding this comment

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

Please remove all leading ^ for version ranges.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@deepakrkris ^ +1 and please also resolve the conflicts for 2 package-lock.json files. LGTM after cleaning up the package.json file :shipit:

Copy link
Contributor

Choose a reason for hiding this comment

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

agreed

Copy link
Contributor Author

@jannyHou jannyHou left a comment

Choose a reason for hiding this comment

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

@deepakrkris LGTM after cleaning up the package.json, package-lock.json files. Thank you!

"@loopback/repository": "1.10.1",
"@loopback/rest": "1.16.6",
"@loopback/rest-explorer": "1.3.1",
"@loopback/security": "^0.1.6",
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@deepakrkris ^ +1 and please also resolve the conflicts for 2 package-lock.json files. LGTM after cleaning up the package.json file :shipit:

jannyHou and others added 5 commits November 11, 2019 12:24
Signed-off-by: DEEPAK RAJAMOHAN <[email protected]>
Signed-off-by: DEEPAK RAJAMOHAN <[email protected]>
Signed-off-by: DEEPAK RAJAMOHAN <[email protected]>
Signed-off-by: DEEPAK RAJAMOHAN <[email protected]>
Signed-off-by: DEEPAK RAJAMOHAN <[email protected]>
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