camper/pkg/uuid/uuid.go

55 lines
1001 B
Go
Raw Normal View History

Add the company’s slug in the URL before company-dependent handlers I really doubt that they are going to use more than a single company, but the application is based on Numerus, that **does** have multiple company, and followed the same architecture and philosophy: use the URL to choose the company to manage, even if the user has a single company. The reason i use the slug instead of the ID is because i do not want to make the ID public in case the application is really used by employees of many unrelated companies: they need not need to guess how many companies there are based on the ID. I validate this slug to be a valid UUID instead of relaying on the query’s empty result because casting a string with a malformed value to UUID results in an error other than data not found. Not with that select, but it would fail with a function parameter, and i want to add that UUID check to all functions that do use slugs. I based uuid.Valid function on Parse() from Google’s uuid package[0] instead of using regular expression, as it was my first idea, because that function is an order of magnitude faster in benchmarks: goos: linux goarch: amd64 pkg: dev.tandem.ws/tandem/numerus/pkg cpu: Intel(R) Core(TM) i5-6200U CPU @ 2.30GHz BenchmarkValidUuid-4 36946050 29.37 ns/op BenchmarkValidUuid_Re-4 3633169 306.70 ns/op The regular expression used for the benchmark was: var re = regexp.MustCompile("^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[8|9|aA|bB][a-fA-F0-9]{3}-[a-fA-F0-9]{12}$") And the input parameter for both functions was the following valid UUID, because most of the time the passed UUID will be valid: "f47ac10b-58cc-0372-8567-0e02b2c3d479" I did not use the uuid package as is, even though it is in Debian’s repository, because i only need to check whether the value is valid, not convert it to a byte array. As far as i know, that package can not do that. Adding the Company struct into auth was not my intention, as it makes little sense name-wise, but i need to have the Company when rendering templates and the company package has templates to render, thus using the company package for the Company struct would create a dependency loop between template and company. I’ve chosen the auth package only because User is also there; User and Company are very much related in this application, but not enough to include the company inside the user, or vice versa, as the User comes from the cookie while the company from the URL. Finally, had to move methodNotAllowed to the http package, as an exported function, because it is used now from other packages, namely campsite. [0]: https://github.com/google/uuid
2023-07-31 16:51:50 +00:00
/*
* SPDX-FileCopyrightText: 2023 jordi fita mas <jfita@peritasoft.com>
* SPDX-License-Identifier: AGPL-3.0-only
*/
package uuid
import (
gohex "encoding/hex"
"fmt"
"dev.tandem.ws/tandem/camper/pkg/hex"
)
Add cover media to campsite types This is the image that is shown at the home page, and maybe other pages in the future. We can not use a static file because this image can be changed by the customer, not us; just like name and description. I decided to keep the actual media content in the database, but to copy this file out to the file system the first time it is accessed. This is because we are going to replicate the database to a public instance that must show exactly the same image, but the customer will update the image from the private instance, behind a firewall. We could also synchronize the folder where they upload the images, the same way we will replicate, but i thought that i would make the whole thing a little more brittle: this way if it can replicate the update of the media, it is impossible to not have its contents; dumping it to a file is to improve subsequent requests to the same media. I use the hex representation of the media’s hash as the URL to the resource, because PostgreSQL’s base64 is not URL save (i.e., it uses RFC2045’s charset that includes the forward slash[0]), and i did not feel necessary write a new function just to slightly reduce the URLs’ length. Before checking if the file exists, i make sure that the given hash is an hex string, like i do for UUID, otherwise any other check is going to fail for sure. I moved out hex.Valid function from UUID to check for valid hex values, but the actual hash check is inside app/media because i doubt it will be used outside that module. [0]: https://datatracker.ietf.org/doc/html/rfc2045#section-6.8
2023-09-10 01:04:18 +00:00
Add the company’s slug in the URL before company-dependent handlers I really doubt that they are going to use more than a single company, but the application is based on Numerus, that **does** have multiple company, and followed the same architecture and philosophy: use the URL to choose the company to manage, even if the user has a single company. The reason i use the slug instead of the ID is because i do not want to make the ID public in case the application is really used by employees of many unrelated companies: they need not need to guess how many companies there are based on the ID. I validate this slug to be a valid UUID instead of relaying on the query’s empty result because casting a string with a malformed value to UUID results in an error other than data not found. Not with that select, but it would fail with a function parameter, and i want to add that UUID check to all functions that do use slugs. I based uuid.Valid function on Parse() from Google’s uuid package[0] instead of using regular expression, as it was my first idea, because that function is an order of magnitude faster in benchmarks: goos: linux goarch: amd64 pkg: dev.tandem.ws/tandem/numerus/pkg cpu: Intel(R) Core(TM) i5-6200U CPU @ 2.30GHz BenchmarkValidUuid-4 36946050 29.37 ns/op BenchmarkValidUuid_Re-4 3633169 306.70 ns/op The regular expression used for the benchmark was: var re = regexp.MustCompile("^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[8|9|aA|bB][a-fA-F0-9]{3}-[a-fA-F0-9]{12}$") And the input parameter for both functions was the following valid UUID, because most of the time the passed UUID will be valid: "f47ac10b-58cc-0372-8567-0e02b2c3d479" I did not use the uuid package as is, even though it is in Debian’s repository, because i only need to check whether the value is valid, not convert it to a byte array. As far as i know, that package can not do that. Adding the Company struct into auth was not my intention, as it makes little sense name-wise, but i need to have the Company when rendering templates and the company package has templates to render, thus using the company package for the Company struct would create a dependency loop between template and company. I’ve chosen the auth package only because User is also there; User and Company are very much related in this application, but not enough to include the company inside the user, or vice versa, as the User comes from the cookie while the company from the URL. Finally, had to move methodNotAllowed to the http package, as an exported function, because it is used now from other packages, namely campsite. [0]: https://github.com/google/uuid
2023-07-31 16:51:50 +00:00
func Valid(s string) bool {
if len(s) != 36 {
return false
}
// it must be of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' {
return false
}
for _, x := range [16]int{
0, 2, 4, 6,
9, 11,
14, 16,
19, 21,
24, 26, 28, 30, 32, 34} {
Add cover media to campsite types This is the image that is shown at the home page, and maybe other pages in the future. We can not use a static file because this image can be changed by the customer, not us; just like name and description. I decided to keep the actual media content in the database, but to copy this file out to the file system the first time it is accessed. This is because we are going to replicate the database to a public instance that must show exactly the same image, but the customer will update the image from the private instance, behind a firewall. We could also synchronize the folder where they upload the images, the same way we will replicate, but i thought that i would make the whole thing a little more brittle: this way if it can replicate the update of the media, it is impossible to not have its contents; dumping it to a file is to improve subsequent requests to the same media. I use the hex representation of the media’s hash as the URL to the resource, because PostgreSQL’s base64 is not URL save (i.e., it uses RFC2045’s charset that includes the forward slash[0]), and i did not feel necessary write a new function just to slightly reduce the URLs’ length. Before checking if the file exists, i make sure that the given hash is an hex string, like i do for UUID, otherwise any other check is going to fail for sure. I moved out hex.Valid function from UUID to check for valid hex values, but the actual hash check is inside app/media because i doubt it will be used outside that module. [0]: https://datatracker.ietf.org/doc/html/rfc2045#section-6.8
2023-09-10 01:04:18 +00:00
if !hex.Valid(s[x], s[x+1]) {
Add the company’s slug in the URL before company-dependent handlers I really doubt that they are going to use more than a single company, but the application is based on Numerus, that **does** have multiple company, and followed the same architecture and philosophy: use the URL to choose the company to manage, even if the user has a single company. The reason i use the slug instead of the ID is because i do not want to make the ID public in case the application is really used by employees of many unrelated companies: they need not need to guess how many companies there are based on the ID. I validate this slug to be a valid UUID instead of relaying on the query’s empty result because casting a string with a malformed value to UUID results in an error other than data not found. Not with that select, but it would fail with a function parameter, and i want to add that UUID check to all functions that do use slugs. I based uuid.Valid function on Parse() from Google’s uuid package[0] instead of using regular expression, as it was my first idea, because that function is an order of magnitude faster in benchmarks: goos: linux goarch: amd64 pkg: dev.tandem.ws/tandem/numerus/pkg cpu: Intel(R) Core(TM) i5-6200U CPU @ 2.30GHz BenchmarkValidUuid-4 36946050 29.37 ns/op BenchmarkValidUuid_Re-4 3633169 306.70 ns/op The regular expression used for the benchmark was: var re = regexp.MustCompile("^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[8|9|aA|bB][a-fA-F0-9]{3}-[a-fA-F0-9]{12}$") And the input parameter for both functions was the following valid UUID, because most of the time the passed UUID will be valid: "f47ac10b-58cc-0372-8567-0e02b2c3d479" I did not use the uuid package as is, even though it is in Debian’s repository, because i only need to check whether the value is valid, not convert it to a byte array. As far as i know, that package can not do that. Adding the Company struct into auth was not my intention, as it makes little sense name-wise, but i need to have the Company when rendering templates and the company package has templates to render, thus using the company package for the Company struct would create a dependency loop between template and company. I’ve chosen the auth package only because User is also there; User and Company are very much related in this application, but not enough to include the company inside the user, or vice versa, as the User comes from the cookie while the company from the URL. Finally, had to move methodNotAllowed to the http package, as an exported function, because it is used now from other packages, namely campsite. [0]: https://github.com/google/uuid
2023-07-31 16:51:50 +00:00
return false
}
}
return true
}
func Parse(src string) (dst [16]byte, err error) {
switch len(src) {
case 36:
src = src[0:8] + src[9:13] + src[14:18] + src[19:23] + src[24:]
case 32:
// dashes already stripped, assume valid
default:
// assume invalid.
return dst, fmt.Errorf("cannot parse UUID %v", src)
}
buf, err := gohex.DecodeString(src)
if err != nil {
return dst, err
}
copy(dst[:], buf)
return dst, err
}