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

cleanup: Use const values to handle layout / entries well known values and generate strings instead of hardcoding them #626

Open
wants to merge 29 commits into
base: main
Choose a base branch
from

Conversation

3v1n0
Copy link
Collaborator

@3v1n0 3v1n0 commented Nov 8, 2024

We've various of hardcoded strings around that are often repeated and makes the code hard to maintain and refactor in a safely way.

So replace them with const values and add some utility functions to generate previously hardcoded values.

I've put them into the internal package, but I feel that some of these definitions would be actually better if exported to be re-usable by authd-oidc-brokers too, or at least to an intermediate library for writing authd brokers.

Meanwhile, this makes authd code a bit less fragile and ideally easier to refactor.

UDENG-5162

@3v1n0 3v1n0 requested a review from a team as a code owner November 8, 2024 03:31
@3v1n0 3v1n0 force-pushed the common-consts branch 2 times, most recently from c6d87de to be360ff Compare November 8, 2024 04:31
@codecov-commenter
Copy link

codecov-commenter commented Nov 8, 2024

Codecov Report

Attention: Patch coverage is 91.69811% with 44 lines in your changes missing coverage. Please review.

Project coverage is 83.27%. Comparing base (b60ae38) to head (57866c1).
Report is 8 commits behind head on main.

Files with missing lines Patch % Lines
internal/examplebroker/broker.go 80.58% 25 Missing and 8 partials ⚠️
internal/services/pam/pam.go 75.00% 4 Missing and 2 partials ⚠️
brokers/auth/mode.go 93.02% 2 Missing and 1 partial ⚠️
pam/internal/adapter/nativemodel.go 95.83% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #626      +/-   ##
==========================================
+ Coverage   83.20%   83.27%   +0.07%     
==========================================
  Files          80       82       +2     
  Lines        8617     8702      +85     
  Branches       75       75              
==========================================
+ Hits         7170     7247      +77     
- Misses       1115     1120       +5     
- Partials      332      335       +3     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.


🚨 Try these New Features:

Copy link
Contributor

@adombeck adombeck left a comment

Choose a reason for hiding this comment

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

I love these changes! I discussed with Didier during the sprint that I wanted to replace the literal strings we use as map keys with constants. I'm glad you already did that now 😄

internal/brokers/auth/consts.go Outdated Show resolved Hide resolved
internal/brokers/auth/consts.go Outdated Show resolved Hide resolved
internal/brokers/auth/consts.go Outdated Show resolved Hide resolved
examplebroker/broker.go Outdated Show resolved Hide resolved
@3v1n0
Copy link
Collaborator Author

3v1n0 commented Nov 8, 2024

I love these changes! I discussed with Didier during the sprint that I wanted to replace the literal strings we use as map keys with constants. I'm glad you already did that now 😄

Yeah, I'm glad you think the same. I had it in the works for a while, maybe we should find a clean way to expose the shared ones with the brokers too, but for now even copying this is fine probably 😅

@adombeck
Copy link
Contributor

adombeck commented Nov 8, 2024

maybe we should find a clean way to expose the shared ones with the brokers too

I was wondering if there is a reason why the broker doesn't depend on authd currently. To me it makes sense to share the constants between authd and the broker. @didrocks, WDYT?

@3v1n0
Copy link
Collaborator Author

3v1n0 commented Nov 11, 2024

maybe we should find a clean way to expose the shared ones with the brokers too

I was wondering if there is a reason why the broker doesn't depend on authd currently. To me it makes sense to share the constants between authd and the broker. @didrocks, WDYT?

Yeah, ideally we should have an intermediate library that provides the shared elements that can be then used both by the example broker and any other go broker out there that we've now and that may show up in future

@3v1n0 3v1n0 requested a review from adombeck November 12, 2024 15:54
Copy link
Member

@denisonbarbosa denisonbarbosa left a comment

Choose a reason for hiding this comment

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

Thanks for addressing this. It's a long-time coming improvement, for sure. Very nice touch dividing the consts into sub-packages so their referencing points towards their meaning.

internal/brokers/auth/consts.go Outdated Show resolved Hide resolved
@didrocks
Copy link
Member

maybe we should find a clean way to expose the shared ones with the brokers too

I was wondering if there is a reason why the broker doesn't depend on authd currently. To me it makes sense to share the constants between authd and the broker. @didrocks, WDYT?

Depending on the amount of elements we share. If those are only a few constants, maybe keeping up to date go.mod for everytime we add something will be time consuming. If this is more, than this, yeah, definitively worth doing!

@adombeck
Copy link
Contributor

If those are only a few constants, maybe keeping up to date go.mod for everytime we add something will be time consuming.

IIUC, these are constants which need to be in sync between authd and the broker, so the alternative is that we update them in both repos every time we change something there. I expect that to be more time consuming than only updating them in authd and then running go get github.com/ubuntu/authd in the broker repo.

@adombeck
Copy link
Contributor

IIUC, these are constants which need to be in sync between authd and the broker, so the alternative is that we update them in both repos every time we change something there. I expect that to be more time consuming than only updating them in authd and then running go get github.com/ubuntu/authd in the broker repo.

A mono repo would make this even simpler of course 😁

@didrocks
Copy link
Member

IIUC, these are constants which need to be in sync between authd and the broker, so the alternative is that we update them in both repos every time we change something there. I expect that to be more time consuming than only updating them in authd and then running go get github.com/ubuntu/authd in the broker repo.

Let’s go with this then! Just try to expose as public only the needed bits please!

@3v1n0
Copy link
Collaborator Author

3v1n0 commented Nov 14, 2024

Let’s go with this then! Just try to expose as public only the needed bits please!

Want to handle it in a different PR or here?

@didrocks
Copy link
Member

Let’s go with this then! Just try to expose as public only the needed bits please!

Want to handle it in a different PR or here?

As you are adding the consts here, let’s expose them directly publicly

@3v1n0
Copy link
Collaborator Author

3v1n0 commented Nov 14, 2024

Ok... Will move them.

Ideally I would have used a different strategy though: we could have used proto files enums and expose them instead (after a rename) and use the same strategy to generate the JSON that is passed through dbus too (more or less how it happens with gdm), as it would have saved us lots of manual management of the types and the usage of map[string][string] in the broker side.

But I'm unsure if that's too late now.

@3v1n0
Copy link
Collaborator Author

3v1n0 commented Nov 20, 2024

@adombeck so things are exported now and I've take the opportunity to make possible to use more structured data on broker side, so please check this again together with ubuntu/authd-oidc-brokers#234

@3v1n0 3v1n0 requested a review from adombeck November 20, 2024 22:26
Avoid building the list manually now that we've consts values and use
this for both optional booleans and supported entries
It can be used to parse optional or required values or entries.

Use this both in broker and example brokers logic
Avoid repeating the same string allover the places
We were using a maps of maps that made code hard to read and maintain
and doing some unneeded conversions to json back and forth.

Avoid this by just using structured data instead
So that we can reuse their ID across the file without further constants
We don't need to expose the proto externally, since we are now re-usable
so if something of it has to be exposed we should do it manually
It's what the oidc broker expects to be set in case the rendering is not
supported, while we were unsetting the value by default.
…se them

We use to create UI options manually and converting them to maps for
being transferred via DBus, it's something we also need to do in the
broker side, so let's expose this publicly for being reused by brokers
too
If some values are unset, there's no point to expose them to the brokers
Now that it has been tested with the manually generated version we can
use the utility functions.
It can be useful for brokers to handle the received messages
…layouts

It adds a convenient way to handle the value as a native type.
These can be used by brokers and authd to parse the results back and
forth from the dbus format.
Now that authd can be imported we should not expose anything that isn't
an API.
@@ -0,0 +1,25 @@
// Package auth contains the authentication related code.
package auth
Copy link
Contributor

Choose a reason for hiding this comment

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

What do you think about moving the the auth and layout packages from brokers/ to pkg/? I find it a bit odd that these packages are below a brokers directory, because the way I see it, these are authd's authentication modes and layouts, not the brokers' - the brokers only choose which of those modes and layouts they support.

Putting packages meant to be imported by other projects below pkg/ is a common pattern in Go projects and IMO brings some structure into our already quite chaotic project structure.

Copy link
Member

Choose a reason for hiding this comment

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

pkg/ while very popular in the past, is an old idiom in Go. It’s clearly not seen as a best practice anymore since around 2018. Brad Fitzpatrick has stated that they stopped using it in the stdlib a long time ago. The conscensus on the popular go project standard project (not official) is to not use it either.

Basically the current stance that I saw many times in conferences and podcasts are:

  • everything private is in internal
  • everything else is public and should be at the root of the project (if possible, like cobra) and can be separated by namespaces (subfolders like golang x/net package).

It’s also the evolution we followed: ubuntu-report had a pkg/ subfolder and never evolved: https://github.com/ubuntu/ubuntu-report while our newer projects don’t.

So I will let you handle the renaming as you want and I think most of what you want to expose should be at to the root of the project. However please, refrain to use a pkg/ subfolder.

Copy link
Contributor

@adombeck adombeck Nov 27, 2024

Choose a reason for hiding this comment

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

pkg/ while very popular in the past, is an old idiom in Go. It’s clearly not seen as a best practice anymore since around 2018. Brad Fitzpatrick has stated that they stopped using it in the stdlib a long time ago. The conscensus on the popular go project standard project (not official) is to not use it either.

Well, that issue contains a lengthy discussion, was never resolved and the project layout still contains pkg, so it's just not true that there is consensus on that.

However, I've also observed the trend that it's used less than it was, and for Go projects which primarily contain packages intended to be used in other projects (like the stdlib), I agree that it does not add any value and just convolutes the import path. I would argue that things are different in more complex projects like authd, which are not primarily intended as a library but want to publish only a few packages for other projects that want to use authd, then it makes sense to put those packages in a directory which makes that purpose clear, and pkg/ still serves that purpose. I don't care enough to continue arguing for it though. Maybe we can find a different name than brokers/ or pkg/ to put those packages in?

Copy link
Member

Choose a reason for hiding this comment

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

Well, that issue contains a lengthy discussion, was never resolved and the project layout still contains pkg, so it's just not true that there is consensus on that.Well, that issue contains a lengthy discussion, was never resolved and the project layout still contains pkg, so it's just not true that there is consensus on that.

Nor than "Putting packages meant to be imported by other projects below pkg/ is a common pattern in Go projects " is true nowday.

To move this forward, what about just moving them to the root of authd? How many do we envision having and do they need separate namespacing?

Comment on lines +17 to +19
type InvalidModeError struct {
error
}
Copy link
Contributor

@adombeck adombeck Nov 26, 2024

Choose a reason for hiding this comment

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

There is no reason to wrap an error inside this. It's more idiomatic to just have the message as the field, and implement Error() and New...() functions.

Suggested change
type InvalidModeError struct {
error
}
type InvalidModeError struct {
Message string
}
// Error is the implementation of the error interface.
func (e InvalidModeError) Error() string {
return e.Message
}
// NewInvalidModeError returns a new InvalidModeError.
func NewInvalidModeError(message string) error {
return InvalidModeError{Message: message}
}

Comment on lines +32 to +35
// UITypeError defines an error for [UIType] errors.
type UITypeError struct {
error
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Same here, we don't need to wrap an error.

@@ -0,0 +1,25 @@
// Package auth contains the authentication related code.
package auth
Copy link
Contributor

@adombeck adombeck Nov 27, 2024

Choose a reason for hiding this comment

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

pkg/ while very popular in the past, is an old idiom in Go. It’s clearly not seen as a best practice anymore since around 2018. Brad Fitzpatrick has stated that they stopped using it in the stdlib a long time ago. The conscensus on the popular go project standard project (not official) is to not use it either.

Well, that issue contains a lengthy discussion, was never resolved and the project layout still contains pkg, so it's just not true that there is consensus on that.

However, I've also observed the trend that it's used less than it was, and for Go projects which primarily contain packages intended to be used in other projects (like the stdlib), I agree that it does not add any value and just convolutes the import path. I would argue that things are different in more complex projects like authd, which are not primarily intended as a library but want to publish only a few packages for other projects that want to use authd, then it makes sense to put those packages in a directory which makes that purpose clear, and pkg/ still serves that purpose. I don't care enough to continue arguing for it though. Maybe we can find a different name than brokers/ or pkg/ to put those packages in?


// Mode is the type to define an authd authentication Mode for brokers usage.
type Mode struct {
*authd.AuthMode
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't like that this adds a dependency on protobuf and grpc to the broker (which doesn't actually use gRPC). I think we need to redefine the fields instead of embedding the protobuf message here.

I also think we can get rid of most of the boilerplate here by using JSON as an intermediate format when converting to/from protobuf and string map. I have some WIP to implement that but some more work is needed and I cannot in good conscience invest more time in it now (while I have other, scheduled tasks to work on). Do you think this could wait until next pulse?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This was my concern too... But I wanted to get it done quickly and this allowed both API consistency and generated code.

So I had also the idea of just reusing it fully privately, but it would still likely lead to the same dependency problem.

Thus, I can definitely refactor this too (I had already thought about, then I decided to see how the shortcut was accepted, at least as an interim solution).

Not sure about passing from json though. It was also one of the reasons why I was using the authd protobufs types (as json conversion is ready and works great as we do for gdm), but I wanted to avoid doing native structs -> variant -> native map -> json -> native structs.

Even though I had it in my options.

One thing I can probably craft is instead a simple generator that reimplements the protobuf structures into something proto-less.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants