Skip to content

Commit

Permalink
model: Introduce a new copyright holder field to data model
Browse files Browse the repository at this point in the history
In German law, the author and the copyright holder can be two seperate
legal entities and therefore also need to be treated separately.

Introduce a new copyright holder field that is now the primary source
for copyright holder information. Authors are still only used as
copyright holders if the `addAuthorsToCopyrights` option is enabled.

For now, all package manager implementations set empty copyright
holders. Filling the copyright holder field is left as an exercise for
future actions. Right now, the only way to add copyright holders is via
curations.

This change resolves oss-review-toolkit#4519

Signed-off-by: Rainer Bieniek <[email protected]>
  • Loading branch information
porsche-rbieniek committed Jun 30, 2022
1 parent 285f018 commit d2718ff
Show file tree
Hide file tree
Showing 34 changed files with 167 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ class SpdxDocumentFileFunTest : WordSpec({
cpe = "cpe:2.3:a:http:curl:7.70.0:*:*:*:*:*:*:*",
definitionFilePath = vcsDir.getPathToRoot(curlPackageFile),
authors = sortedSetOf("Daniel Stenberg ([email protected])"),
copyrightHolders = sortedSetOf(),
declaredLicenses = sortedSetOf("curl"),
vcs = VcsInfo(
type = VcsType.GIT,
Expand All @@ -120,6 +121,7 @@ class SpdxDocumentFileFunTest : WordSpec({
cpe = "cpe:2.3:a:a-name:openssl:1.1.1g:*:*:*:*:*:*:*",
definitionFilePath = vcsDir.getPathToRoot(opensslPackageFile),
authors = sortedSetOf("OpenSSL Development Team"),
copyrightHolders = sortedSetOf(),
declaredLicenses = sortedSetOf("Apache-2.0"),
vcs = VcsInfo(
type = VcsType.GIT,
Expand All @@ -141,6 +143,7 @@ class SpdxDocumentFileFunTest : WordSpec({
cpe = "cpe:/a:compress:zlib:1.2.11:::en-us",
definitionFilePath = vcsDir.getPathToRoot(zlibPackageFile),
authors = sortedSetOf("Jean-loup Gailly", "Mark Adler"),
copyrightHolders = sortedSetOf(),
declaredLicenses = sortedSetOf("Zlib"),
vcs = VcsInfo(
type = VcsType.GIT,
Expand Down
2 changes: 2 additions & 0 deletions analyzer/src/main/kotlin/managers/Bower.kt
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ class Bower(
Package(
id = parsePackageId(node),
authors = parseAuthors(node),
copyrightHolders = sortedSetOf(),
declaredLicenses = parseDeclaredLicenses(node),
description = node["pkgMeta"]["description"].textValueOrEmpty(),
homepageUrl = node["pkgMeta"]["homepage"].textValueOrEmpty(),
Expand Down Expand Up @@ -252,6 +253,7 @@ class Bower(
id = projectPackage.id,
definitionFilePath = VersionControlSystem.getPathInfo(definitionFile).path,
authors = projectPackage.authors,
copyrightHolders = sortedSetOf(),
declaredLicenses = projectPackage.declaredLicenses,
vcs = projectPackage.vcs,
vcsProcessed = processProjectVcs(workingDir, projectPackage.vcs, projectPackage.homepageUrl),
Expand Down
2 changes: 2 additions & 0 deletions analyzer/src/main/kotlin/managers/Bundler.kt
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ class Bundler(
id = projectId,
definitionFilePath = VersionControlSystem.getPathInfo(definitionFile).path,
authors = authors,
copyrightHolders = sortedSetOf(),
declaredLicenses = declaredLicenses.toSortedSet(),
vcs = VcsInfo.EMPTY,
vcsProcessed = processProjectVcs(workingDir, VcsInfo.EMPTY, homepageUrl),
Expand Down Expand Up @@ -264,6 +265,7 @@ class Bundler(
return Package(
id = gemId,
authors = gemSpec.authors,
copyrightHolders = sortedSetOf(),
declaredLicenses = gemSpec.declaredLicenses,
description = gemSpec.description,
homepageUrl = gemSpec.homepageUrl,
Expand Down
2 changes: 2 additions & 0 deletions analyzer/src/main/kotlin/managers/Cargo.kt
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ class Cargo(
id = projectPkg.id,
definitionFilePath = VersionControlSystem.getPathInfo(definitionFile).path,
authors = authors,
copyrightHolders = sortedSetOf(),
declaredLicenses = projectPkg.declaredLicenses,
declaredLicensesProcessed = processDeclaredLicenses(projectPkg.declaredLicenses),
vcs = projectPkg.vcs,
Expand Down Expand Up @@ -272,6 +273,7 @@ private fun parsePackage(node: JsonNode, hashes: Map<String, String>): Package {
return Package(
id = parsePackageId(node),
authors = parseAuthors(node["authors"]),
copyrightHolders = sortedSetOf(),
declaredLicenses = declaredLicenses,
declaredLicensesProcessed = declaredLicensesProcessed,
description = node["description"].textValueOrEmpty(),
Expand Down
4 changes: 4 additions & 0 deletions analyzer/src/main/kotlin/managers/Carthage.kt
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ class Carthage(
),
definitionFilePath = VersionControlSystem.getPathInfo(definitionFile).path,
authors = sortedSetOf(),
copyrightHolders = sortedSetOf(),
declaredLicenses = sortedSetOf(),
vcs = VcsInfo.EMPTY,
vcsProcessed = processProjectVcs(workingDir, VcsInfo.EMPTY),
Expand Down Expand Up @@ -190,6 +191,7 @@ class Carthage(
version = revision
),
authors = sortedSetOf(),
copyrightHolders = sortedSetOf(),
declaredLicenses = sortedSetOf(),
description = "",
homepageUrl = projectUrl.removeSuffix(".git"),
Expand All @@ -214,6 +216,7 @@ class Carthage(
version = revision
),
authors = sortedSetOf(),
copyrightHolders = sortedSetOf(),
declaredLicenses = sortedSetOf(),
description = "",
homepageUrl = "",
Expand All @@ -232,6 +235,7 @@ class Carthage(
version = revision
),
authors = sortedSetOf(),
copyrightHolders = sortedSetOf(),
declaredLicenses = sortedSetOf(),
description = "",
homepageUrl = "",
Expand Down
2 changes: 2 additions & 0 deletions analyzer/src/main/kotlin/managers/CocoaPods.kt
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ class CocoaPods(
),
definitionFilePath = VersionControlSystem.getPathInfo(definitionFile).path,
authors = sortedSetOf(),
copyrightHolders = sortedSetOf(),
declaredLicenses = sortedSetOf(),
vcs = VcsInfo.EMPTY,
vcsProcessed = processProjectVcs(workingDir),
Expand Down Expand Up @@ -186,6 +187,7 @@ class CocoaPods(
return Package(
id = id,
authors = sortedSetOf(),
copyrightHolders = sortedSetOf(),
declaredLicenses = podspec.license.takeUnless { it.isEmpty() }?.let { sortedSetOf(it) } ?: sortedSetOf(),
description = podspec.summary,
homepageUrl = podspec.homepage,
Expand Down
2 changes: 2 additions & 0 deletions analyzer/src/main/kotlin/managers/Composer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@ class Composer(
),
definitionFilePath = VersionControlSystem.getPathInfo(definitionFile).path,
authors = parseAuthors(json),
copyrightHolders = sortedSetOf(),
declaredLicenses = parseDeclaredLicenses(json),
vcs = vcs,
vcsProcessed = processProjectVcs(definitionFile.parentFile, vcs, homepageUrl),
Expand Down Expand Up @@ -265,6 +266,7 @@ class Composer(
version = version
),
authors = parseAuthors(pkgInfo),
copyrightHolders = sortedSetOf(),
declaredLicenses = parseDeclaredLicenses(pkgInfo),
description = pkgInfo["description"].textValueOrEmpty(),
homepageUrl = homepageUrl,
Expand Down
4 changes: 4 additions & 0 deletions analyzer/src/main/kotlin/managers/Conan.kt
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ class Conan(
id = projectPackage.id,
definitionFilePath = VersionControlSystem.getPathInfo(definitionFile).path,
authors = projectPackage.authors,
copyrightHolders = sortedSetOf(),
declaredLicenses = projectPackage.declaredLicenses,
vcs = projectPackage.vcs,
vcsProcessed = processProjectVcs(
Expand Down Expand Up @@ -288,6 +289,7 @@ class Conan(
return Package(
id = id,
authors = parseAuthors(node),
copyrightHolders = sortedSetOf(),
declaredLicenses = parseDeclaredLicenses(node),
description = parsePackageField(node, workingDir, "description"),
homepageUrl = homepageUrl,
Expand Down Expand Up @@ -427,6 +429,7 @@ class Conan(
version = inspectField(definitionFile.name, workingDir, "version")
),
authors = parseAuthors(node),
copyrightHolders = sortedSetOf(),
declaredLicenses = parseDeclaredLicenses(node),
description = inspectField(definitionFile.name, workingDir, "description"),
homepageUrl = node["homepage"].textValueOrEmpty(),
Expand All @@ -447,6 +450,7 @@ class Conan(
version = ""
),
authors = parseAuthors(node),
copyrightHolders = sortedSetOf(),
declaredLicenses = parseDeclaredLicenses(node),
description = "",
homepageUrl = node["homepage"].textValueOrEmpty(),
Expand Down
2 changes: 2 additions & 0 deletions analyzer/src/main/kotlin/managers/GoDep.kt
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ class GoDep(
val pkg = Package(
id = Identifier(managerName, "", name, version),
authors = sortedSetOf(),
copyrightHolders = sortedSetOf(),
declaredLicenses = sortedSetOf(),
description = "",
homepageUrl = "",
Expand Down Expand Up @@ -171,6 +172,7 @@ class GoDep(
),
definitionFilePath = VersionControlSystem.getPathInfo(definitionFile).path,
authors = sortedSetOf(),
copyrightHolders = sortedSetOf(),
declaredLicenses = sortedSetOf(),
vcs = VcsInfo.EMPTY,
vcsProcessed = projectVcs,
Expand Down
2 changes: 2 additions & 0 deletions analyzer/src/main/kotlin/managers/GoMod.kt
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ class GoMod(
),
definitionFilePath = VersionControlSystem.getPathInfo(definitionFile).path,
authors = sortedSetOf(), // Go mod doesn't support author information.
copyrightHolders = sortedSetOf(), // Go mod doesn't support copyright statements.
declaredLicenses = sortedSetOf(), // Go mod doesn't support declared licenses.
vcs = projectVcs,
vcsProcessed = projectVcs,
Expand Down Expand Up @@ -274,6 +275,7 @@ class GoMod(
return Package(
id = Identifier(managerName, "", id.name, id.version),
authors = sortedSetOf(), // Go mod doesn't support author information.
copyrightHolders = sortedSetOf(), // Go mod doesn't support copyright information as well
declaredLicenses = sortedSetOf(), // Go mod doesn't support declared licenses.
description = "",
homepageUrl = "",
Expand Down
1 change: 1 addition & 0 deletions analyzer/src/main/kotlin/managers/Gradle.kt
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,7 @@ class Gradle(
id = projectId,
definitionFilePath = VersionControlSystem.getPathInfo(definitionFile).path,
authors = sortedSetOf(),
copyrightHolders = sortedSetOf(),
declaredLicenses = sortedSetOf(),
vcs = VcsInfo.EMPTY,
vcsProcessed = processProjectVcs(definitionFile.parentFile),
Expand Down
1 change: 1 addition & 0 deletions analyzer/src/main/kotlin/managers/Maven.kt
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ class Maven(
id = projectId,
definitionFilePath = VersionControlSystem.getPathInfo(definitionFile).path,
authors = MavenSupport.parseAuthors(mavenProject),
copyrightHolders = sortedSetOf(),
declaredLicenses = declaredLicenses,
declaredLicensesProcessed = declaredLicensesProcessed,
vcs = vcsFromPackage,
Expand Down
2 changes: 2 additions & 0 deletions analyzer/src/main/kotlin/managers/Npm.kt
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,7 @@ open class Npm(
val module = Package(
id = id,
authors = authors,
copyrightHolders = sortedSetOf(),
declaredLicenses = declaredLicenses,
description = description,
homepageUrl = homepageUrl,
Expand Down Expand Up @@ -623,6 +624,7 @@ open class Npm(
),
definitionFilePath = VersionControlSystem.getPathInfo(packageJson).path,
authors = authors,
copyrightHolders = sortedSetOf(),
declaredLicenses = declaredLicenses,
vcs = vcsFromPackage,
vcsProcessed = processProjectVcs(projectDir, vcsFromPackage, homepageUrl),
Expand Down
2 changes: 2 additions & 0 deletions analyzer/src/main/kotlin/managers/Pip.kt
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,7 @@ class Pip(
),
definitionFilePath = VersionControlSystem.getPathInfo(definitionFile).path,
authors = authors,
copyrightHolders = sortedSetOf(),
declaredLicenses = declaredLicenses,
vcs = VcsInfo.EMPTY,
vcsProcessed = processProjectVcs(workingDir, VcsInfo.EMPTY, setupHomepage),
Expand Down Expand Up @@ -737,6 +738,7 @@ private fun Package.enrichWith(other: Package?): Package =
homepageUrl = homepageUrl.takeUnless { it.isBlank() } ?: other.homepageUrl,
description = description.takeUnless { it.isBlank() } ?: other.description,
authors = authors.takeUnless { it.isEmpty() } ?: other.authors,
copyrightHolders = copyrightHolders.takeUnless { it.isEmpty() } ?: other.copyrightHolders,
declaredLicenses = declaredLicenses.takeUnless { it.isEmpty() } ?: other.declaredLicenses,
declaredLicensesProcessed = declaredLicensesProcessed.takeUnless { declaredLicenses.isEmpty() }
?: other.declaredLicensesProcessed,
Expand Down
2 changes: 2 additions & 0 deletions analyzer/src/main/kotlin/managers/Pub.kt
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,7 @@ class Pub(
),
definitionFilePath = VersionControlSystem.getPathInfo(definitionFile).path,
authors = authors,
copyrightHolders = sortedSetOf(),
// Pub does not declare any licenses in the pubspec files, therefore we keep this empty.
declaredLicenses = sortedSetOf(),
vcs = vcs,
Expand Down Expand Up @@ -530,6 +531,7 @@ class Pub(
packages[id] = Package(
id,
authors = authors,
copyrightHolders = sortedSetOf(),
// Pub does not declare any licenses in the pubspec files, therefore we keep this empty.
declaredLicenses = sortedSetOf(),
description = description,
Expand Down
2 changes: 2 additions & 0 deletions analyzer/src/main/kotlin/managers/SpdxDocumentFile.kt
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,7 @@ class SpdxDocumentFile(
purl = locateExternalReference(SpdxExternalReference.Type.Purl) ?: id.toPurl(),
cpe = locateCpe(),
authors = originator.wrapPresentInSortedSet(),
copyrightHolders = sortedSetOf(),
declaredLicenses = sortedSetOf(licenseDeclared),
concludedLicense = getConcludedLicense(),
description = packageDescription,
Expand Down Expand Up @@ -467,6 +468,7 @@ class SpdxDocumentFile(
cpe = projectPackage.locateCpe(),
definitionFilePath = VersionControlSystem.getPathInfo(definitionFile).path,
authors = projectPackage.originator.wrapPresentInSortedSet(),
copyrightHolders = sortedSetOf(),
declaredLicenses = sortedSetOf(projectPackage.licenseDeclared),
vcs = processProjectVcs(definitionFile.parentFile, VcsInfo.EMPTY),
homepageUrl = projectPackage.homepage.mapNotPresentToEmpty(),
Expand Down
2 changes: 2 additions & 0 deletions analyzer/src/main/kotlin/managers/Stack.kt
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ class Stack(
id = projectId,
definitionFilePath = VersionControlSystem.getPathInfo(definitionFile).path,
authors = projectPackage.authors,
copyrightHolders = sortedSetOf(),
declaredLicenses = projectPackage.declaredLicenses,
vcs = projectPackage.vcs,
vcsProcessed = processProjectVcs(workingDir, projectPackage.vcs, projectPackage.homepageUrl),
Expand Down Expand Up @@ -334,6 +335,7 @@ class Stack(
.map(String::trim)
.filter(String::isNotEmpty)
.mapTo(sortedSetOf(), ::parseAuthorString),
copyrightHolders = sortedSetOf(),
declaredLicenses = map["license"]?.let { sortedSetOf(it) } ?: sortedSetOf(),
description = map["description"].orEmpty(),
homepageUrl = homepageUrl,
Expand Down
1 change: 1 addition & 0 deletions analyzer/src/main/kotlin/managers/utils/MavenSupport.kt
Original file line number Diff line number Diff line change
Expand Up @@ -720,6 +720,7 @@ class MavenSupport(private val workspaceReader: WorkspaceReader) {
version = mavenProject.version
),
authors = parseAuthors(mavenProject),
copyrightHolders = sortedSetOf(),
declaredLicenses = declaredLicenses,
declaredLicensesProcessed = declaredLicensesProcessed,
description = mavenProject.description.orEmpty(),
Expand Down
2 changes: 2 additions & 0 deletions analyzer/src/main/kotlin/managers/utils/NuGetSupport.kt
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ class NuGetSupport(serviceIndexUrls: List<String> = listOf(DEFAULT_SERVICE_INDEX
Package(
id = getIdentifier(id, version),
authors = parseAuthors(all.spec),
copyrightHolders = sortedSetOf(),
declaredLicenses = parseLicenses(all.spec),
description = description.orEmpty(),
homepageUrl = homepageUrl,
Expand Down Expand Up @@ -370,6 +371,7 @@ private fun PackageManager.getProject(
),
definitionFilePath = VersionControlSystem.getPathInfo(definitionFile).path,
authors = parseAuthors(spec),
copyrightHolders = sortedSetOf(),
declaredLicenses = parseLicenses(spec),
vcs = VcsInfo.EMPTY,
vcsProcessed = PackageManager.processProjectVcs(workingDir),
Expand Down
4 changes: 4 additions & 0 deletions analyzer/src/test/kotlin/managers/CarthageTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ class CarthageTest : WordSpec() {
id.type shouldBe "Carthage"
vcs.url shouldBe "https://github.com/Alamofire/AlamofireImage.git"
vcs.revision shouldBe "3.2.0"
copyrightHolders shouldBe emptySet()
}
}
}
Expand All @@ -67,6 +68,7 @@ class CarthageTest : WordSpec() {
vcs.type shouldBe VcsType.GIT
vcs.url shouldBe "https://host.tld/path/to/project.git"
vcs.revision shouldBe "1.0.0"
copyrightHolders shouldBe emptySet()
}
}
}
Expand All @@ -85,6 +87,7 @@ class CarthageTest : WordSpec() {
id.type shouldBe "Carthage"
id.name shouldBe "spec"
binaryArtifact.url shouldBe "https://host.tld/path/to/binary/dependency.zip"
copyrightHolders shouldBe emptySet()
}
}
}
Expand All @@ -102,6 +105,7 @@ class CarthageTest : WordSpec() {
size shouldBe 3
forEach {
it.id.type shouldBe "Carthage"
it.copyrightHolders shouldBe emptySet()
}
count { "user/project" in it.vcs.url } shouldBe 1
count { "user-2/project_2" in it.vcs.url } shouldBe 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ class NpmDependencyHandlerTest : StringSpec({
id shouldBe Identifier("NPM", "", "bonjour", "3.5.0")
declaredLicenses should containExactly("MIT")
authors should containExactly("Thomas Watson Steen")
copyrightHolders shouldBe emptySet<String>()
homepageUrl shouldBe "https://github.com/watson/bonjour/local"
description shouldBe "A Bonjour/Zeroconf implementation in pure JavaScript (local)"
}
Expand Down
9 changes: 9 additions & 0 deletions model/src/main/kotlin/Package.kt
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,13 @@ data class Package(
@JsonInclude(JsonInclude.Include.NON_DEFAULT)
val authors: SortedSet<String> = sortedSetOf(),

/**
* The list of copyright holders declared for this package. These might be different from the list of [authors]
* if all or parts of the copyright has been transferred.
*/
@JsonInclude(JsonInclude.Include.NON_DEFAULT)
val copyrightHolders: SortedSet<String> = sortedSetOf(),

/**
* The list of licenses the authors have declared for this package. This does not necessarily correspond to the
* licenses as detected by a scanner. Both need to be taken into account for any conclusions.
Expand Down Expand Up @@ -138,6 +145,7 @@ data class Package(
id = Identifier.EMPTY,
purl = "",
authors = sortedSetOf(),
copyrightHolders = sortedSetOf(),
declaredLicenses = sortedSetOf(),
declaredLicensesProcessed = ProcessedDeclaredLicense.EMPTY,
concludedLicense = null,
Expand Down Expand Up @@ -167,6 +175,7 @@ data class Package(

return PackageCurationData(
authors = authors.takeIf { it != other.authors },
copyrightHolders = copyrightHolders.takeIf { it != other.copyrightHolders },
description = description.takeIf { it != other.description },
homepageUrl = homepageUrl.takeIf { it != other.homepageUrl },
binaryArtifact = binaryArtifact.takeIf { it != other.binaryArtifact },
Expand Down

0 comments on commit d2718ff

Please sign in to comment.