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

GitHub Actions Secrets (Repo and Org) simple CRUD operations ( and Get Public Key for Repo and Org as dependency) #1496

Draft
wants to merge 11 commits into
base: main
Choose a base branch
from
12 changes: 12 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,18 @@
<version>1.7.36</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.goterl</groupId>
<artifactId>lazysodium-java</artifactId>
<version>5.1.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId>
<version>5.12.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<repositories>
<repository>
Expand Down
28 changes: 28 additions & 0 deletions src/main/java/org/kohsuke/github/GHOrgPublicKey.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package org.kohsuke.github;

/**
* A GitHub organization's public key.
*
* @author João Almeida
*/
class GHOrgPublicKey extends GHPublicKey {
/**
* Organization that the public key belongs to.
*/
transient GHOrganization organization;

GHOrgPublicKey wrap(GHOrganization owner) {
this.organization = owner;
return this;
}

@Override
GitHub root() {
return organization.root();
}

@Override
String getApiRoute() {
return String.format("/orgs/%s/actions/secrets/public-key", organization.getLogin());
}
}
71 changes: 71 additions & 0 deletions src/main/java/org/kohsuke/github/GHOrgSecret.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package org.kohsuke.github;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;

import java.net.URL;

/**
* A secret in an organization.
*
* @author João Almeida
*/
@SuppressFBWarnings(value = { "UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD", "NP_UNWRITTEN_FIELD" },
justification = "JSON API")
public class GHOrgSecret extends GHObject {

/**
* Organization that the secret belongs to.
*/
transient GHOrganization organization;

String name;
String visibility;
String selectedRepositoriesUrl;

/**
* Gets the name of the secret.
*
* @return the name of the secret
*/
public String getName() {
return name;
}

/**
* Gets the visibility of the secret.
*
* @return the visibility of the secret
*/
public String getVisibility() {
return visibility;
}

/**
* Gets the repositories url that the secret can be accessed from.
*
* @return the repositories url that the secret can be accessed from
*/
public String getRepositoriesUrl() {
return selectedRepositoriesUrl;
}

/**
* @deprecated This object has no HTML URL.
*/
@Override
public URL getHtmlUrl() {
return null;
}

/**
* Wrap up the secret.
*
* @param owner
* the owner of the secret
* @return the secret
*/
GHOrgSecret wrapUp(GHOrganization owner) {
this.organization = owner;
return this;
}
}
114 changes: 114 additions & 0 deletions src/main/java/org/kohsuke/github/GHOrganization.java
Original file line number Diff line number Diff line change
Expand Up @@ -721,4 +721,118 @@ public GHHook createWebHook(URL url, Collection<GHEvent> events) throws IOExcept
public GHHook createWebHook(URL url) throws IOException {
return createWebHook(url, null);
}

/**
* Gets an organization public key, which is needed to encrypt secrets.
* "https://docs.github.com/en/rest/actions/secrets#get-an-organization-public-key"
*
* @return the public key
* @throws IOException
* the io exception
*/
public GHPublicKey getPublicKey() throws IOException {
return GHPublicKeys.orgContext(this).getPublicKey();
}

/**
* Gets a single organization secret without revealing its encrypted value.
* "https://docs.github.com/rest/actions/secrets#get-an-organization-secret"
*
* @param secretName
* the name of the secret
* @return the secret
* @throws IOException
* the io exception
*/
public GHOrgSecret getSecret(String secretName) throws IOException {
return root().createRequest()
.withUrlPath(String.format("/orgs/%s/actions/secrets/%s", login, secretName))
.fetch(GHOrgSecret.class)
.wrapUp(this);
}

/**
* Creates or updates an organization secret with an encrypted value.
* "https://docs.github.com/rest/reference/actions#create-or-update-an-organization-secret"
*
* @param secretName
* the name of the secret
* @param encryptedValue
* The encrypted value for this secret
* @param publicKeyId
* The id of the Public Key used to encrypt this secret
* @param visibility
* Which type of organization repositories have access to the organization secret. Can be one of: "all",
* "private", "selected"
* @param selected_repository_ids
* An array of repository ids that can access the organization secret. You can only provide a list of
* repository ids when the visibility is set to selected.
* @throws IOException
* the io exception
*/
public void createSecret(String secretName,
String encryptedValue,
String publicKeyId,
String visibility,
long[] selected_repository_ids) throws IOException {
if (visibility.equals("selected") && selected_repository_ids == null) {
throw new IllegalArgumentException(
"You must provide a list of repository ids when the visibility is set to selected");
}
root().createRequest()
.method("PUT")
.with("encrypted_value", encryptedValue)
.with("key_id", publicKeyId)
.with("visibility", visibility)
.with("selected_repository_ids", selected_repository_ids)
.withUrlPath(String.format("/orgs/%s/actions/secrets/%s", login, secretName))
.send();
}

/**
* Creates or updates an organization secret with an encrypted value.
* "https://docs.github.com/rest/reference/actions#create-or-update-an-organization-secret"
*
* @param secretName
* the name of the secret
* @param encryptedValue
* The encrypted value for this secret
* @param publicKeyId
* The id of the Public Key used to encrypt this secret
* @param visibility
* Which type of organization repositories have access to the organization secret. Can be one of: "all",
* "private", "selected"
* @throws IOException
* the io exception
*/
public void createSecret(String secretName, String encryptedValue, String publicKeyId, String visibility)
throws IOException {
if (visibility.equals("selected")) {
throw new IllegalArgumentException(
"You must provide a list of repository ids when the visibility is set to selected");
}
root().createRequest()
.method("PUT")
.with("encrypted_value", encryptedValue)
.with("key_id", publicKeyId)
.with("visibility", visibility)
.withUrlPath(String.format("/orgs/%s/actions/secrets/%s", login, secretName))
.send();
}

/**
* Deletes a secret in an organization using the secret name.
* "https://docs.github.com/rest/actions/secrets#delete-an-organization-secret"
*
* @param secretName
* the name of the secret
* @throws IOException
* the io exception
*/
public void deleteSecret(String secretName) throws IOException {
root().createRequest()
.method("DELETE")
.withUrlPath(String.format("/orgs/%s/actions/secrets/%s", login, secretName))
.send();
}
}
37 changes: 37 additions & 0 deletions src/main/java/org/kohsuke/github/GHPublicKey.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package org.kohsuke.github;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;

import java.net.URL;

/**
* The type GHPublicKey.
*
* @author João Almeida
*/
@SuppressFBWarnings(value = { "UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD", "NP_UNWRITTEN_FIELD" },
justification = "JSON API")
public abstract class GHPublicKey extends GHObject {
private String keyId;
private String key;

/**
* @deprecated This object has no HTML URL.
*/
@Override
public URL getHtmlUrl() {
return null;
}

public String getKeyId() {
return keyId;
}

public String getKey() {
return key;
}

abstract GitHub root();

abstract String getApiRoute();
}
106 changes: 106 additions & 0 deletions src/main/java/org/kohsuke/github/GHPublicKeys.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package org.kohsuke.github;

import java.io.IOException;
import java.util.*;

/**
* Utility class for managing public keys; removes duplication between GHOrganization and GHRepository functionality
*/
class GHPublicKeys {

static abstract class Context extends GitHubInteractiveObject {

private Context(GitHub root) {
super(root);
}

/**
* Gets public Key.
*
* @return the public key
* @throws IOException
* the io exception
*/
public GHPublicKey getPublicKey() throws IOException {
GHPublicKey key = root().createRequest().withUrlPath(collection()).fetch(clazz());
return wrap(key);
}

abstract String collection();

abstract Class<? extends GHPublicKey[]> collectionClass();

abstract Class<? extends GHPublicKey> clazz();

abstract GHPublicKey wrap(GHPublicKey key);

}

private static class RepoContext extends Context {
private final GHRepository repository;
private final GHUser owner;

private RepoContext(GHRepository repository, GHUser owner) {
super(repository.root());
this.repository = repository;
this.owner = owner;
}

@Override
String collection() {
return String.format("/repos/%s/%s/actions/secrets/public-key", owner.getLogin(), repository.getName());
}

@Override
Class<? extends GHPublicKey[]> collectionClass() {
return GHRepositoryPublicKey[].class;
}

@Override
Class<? extends GHPublicKey> clazz() {
return GHRepositoryPublicKey.class;
}

@Override
GHPublicKey wrap(GHPublicKey key) {
return ((GHRepositoryPublicKey) key).wrap(repository);
}
}

private static class OrgContext extends Context {
private final GHOrganization organization;

private OrgContext(GHOrganization organization) {
super(organization.root());
this.organization = organization;
}

@Override
String collection() {
return String.format("/orgs/%s/actions/secrets/public-key", organization.getLogin());
}

@Override
Class<? extends GHPublicKey[]> collectionClass() {
return GHOrgPublicKey[].class;
}

@Override
Class<? extends GHPublicKey> clazz() {
return GHOrgPublicKey.class;
}

@Override
GHPublicKey wrap(GHPublicKey key) {
return ((GHOrgPublicKey) key).wrap(organization);
}
}

static Context repoContext(GHRepository repository, GHUser owner) {
return new RepoContext(repository, owner);
}

static Context orgContext(GHOrganization organization) {
return new OrgContext(organization);
}
}