diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml
index 15326153..50b066cf 100644
--- a/.github/workflows/maven.yml
+++ b/.github/workflows/maven.yml
@@ -10,6 +10,10 @@ jobs:
 
     runs-on: ubuntu-latest
 
+    env:
+      HAVE_SONAR_TOKEN: ${{ secrets.SONAR_TOKEN != '' }}
+      HAVE_DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN != '' }}
+
     steps:
       - name: Checkout
         uses: actions/checkout@v2
@@ -41,6 +45,7 @@ jobs:
           key: ${{ runner.os }}-m2-${{ secrets.CACHE_VERSION }}-${{ hashFiles('**/pom.xml') }}
           restore-keys: ${{ runner.os }}-m2-${{ secrets.CACHE_VERSION }}
       - name: Login to Docker Hub
+        if: ${{ env.HAVE_DOCKERHUB_TOKEN == 'true' }}
         uses: docker/login-action@v1
         with:
           username: ${{ secrets.DOCKERHUB_USERNAME }}
@@ -64,6 +69,7 @@ jobs:
       - name: Set folders ownership back to current user
         run: sudo chown -R $(id -u):$(id -g) $GITHUB_WORKSPACE && sudo chown -R $(id -u):$(id -g) $HOME
       - name: Sonar analysis
+        if: ${{ env.HAVE_SONAR_TOKEN == 'true' }}
         run: mvn sonar:sonar -Dsonar.host.url=$SONAR_URL -Dsonar.projectKey=$SONAR_PROJECTKEY -Dsonar.organization=$SONAR_ORGANIZATION
         env:
           SONAR_PROJECTKEY: GReD-Clermont_simple-omero-client
diff --git a/src/main/java/fr/igred/omero/repository/DatasetWrapper.java b/src/main/java/fr/igred/omero/repository/DatasetWrapper.java
index 59c095d8..7e74c200 100644
--- a/src/main/java/fr/igred/omero/repository/DatasetWrapper.java
+++ b/src/main/java/fr/igred/omero/repository/DatasetWrapper.java
@@ -159,6 +159,26 @@ protected String annotationLinkType() {
     }
 
 
+    /**
+     * Retrieves the projects containing this dataset.
+     *
+     * @param client The client handling the connection.
+     *
+     * @return See above.
+     *
+     * @throws OMEROServerError   Server error.
+     * @throws ServiceException   Cannot connect to OMERO.
+     * @throws AccessException    Cannot access data.
+     * @throws ExecutionException A Facility can't be retrieved or instantiated.
+     */
+    public List<ProjectWrapper> getProjects(Client client)
+    throws OMEROServerError, ServiceException, AccessException, ExecutionException {
+        List<IObject> os = client.findByQuery("select link.parent from ProjectDatasetLink as link " +
+                                              "where link.child=" + getId());
+        return client.getProjects(os.stream().map(IObject::getId).map(RLong::getValue).toArray(Long[]::new));
+    }
+
+
     /**
      * Gets all images in the dataset available from OMERO.
      *
diff --git a/src/main/java/fr/igred/omero/repository/ImageWrapper.java b/src/main/java/fr/igred/omero/repository/ImageWrapper.java
index 41ac8eb0..6993b45d 100644
--- a/src/main/java/fr/igred/omero/repository/ImageWrapper.java
+++ b/src/main/java/fr/igred/omero/repository/ImageWrapper.java
@@ -32,6 +32,7 @@
 import ij.process.LUT;
 import loci.common.DataTools;
 import loci.formats.FormatTools;
+import omero.RLong;
 import omero.ServerError;
 import omero.api.RenderingEnginePrx;
 import omero.api.ThumbnailStorePrx;
@@ -164,6 +165,49 @@ protected String annotationLinkType() {
     }
 
 
+    /**
+     * Retrieves the projects containing this image
+     *
+     * @param client The client handling the connection.
+     *
+     * @return See above.
+     *
+     * @throws OMEROServerError   Server error.
+     * @throws ServiceException   Cannot connect to OMERO.
+     * @throws AccessException    Cannot access data.
+     * @throws ExecutionException A Facility can't be retrieved or instantiated.
+     */
+    public List<ProjectWrapper> getProjects(Client client)
+    throws OMEROServerError, ServiceException, AccessException, ExecutionException {
+        List<DatasetWrapper> datasets = getDatasets(client);
+        List<ProjectWrapper> projects = new ArrayList<>(datasets.size());
+        for (DatasetWrapper dataset : datasets) {
+            projects.addAll(dataset.getProjects(client));
+        }
+        return projects;
+    }
+
+
+    /**
+     * Retrieves the datasets containing this image
+     *
+     * @param client The client handling the connection.
+     *
+     * @return See above.
+     *
+     * @throws OMEROServerError   Server error.
+     * @throws ServiceException   Cannot connect to OMERO.
+     * @throws AccessException    Cannot access data.
+     * @throws ExecutionException A Facility can't be retrieved or instantiated.
+     */
+    public List<DatasetWrapper> getDatasets(Client client)
+    throws OMEROServerError, ServiceException, AccessException, ExecutionException {
+        List<IObject> os = client.findByQuery("select link.parent from DatasetImageLink as link " +
+                                              "where link.child=" + getId());
+        return client.getDatasets(os.stream().map(IObject::getId).map(RLong::getValue).toArray(Long[]::new));
+    }
+
+
     /**
      * Links a ROI to the image in OMERO
      * <p> DO NOT USE IT IF A SHAPE WAS DELETED !!!
diff --git a/src/test/java/fr/igred/omero/repository/DatasetTest.java b/src/test/java/fr/igred/omero/repository/DatasetTest.java
index dbe2452c..7ac0cc88 100644
--- a/src/test/java/fr/igred/omero/repository/DatasetTest.java
+++ b/src/test/java/fr/igred/omero/repository/DatasetTest.java
@@ -256,6 +256,12 @@ public void testAddAndRemoveTagFromDataset() throws Exception {
     }
 
 
+    @Test
+    public void testGetProjects() throws Exception {
+        assertEquals(PROJECT1.id, client.getImage(DATASET1.id).getProjects(client).get(0).getId());
+    }
+
+
     @Test
     public void testGetImagesInDataset() throws Exception {
         DatasetWrapper dataset = client.getDataset(DATASET1.id);
diff --git a/src/test/java/fr/igred/omero/repository/ImageTest.java b/src/test/java/fr/igred/omero/repository/ImageTest.java
index d9f05f4a..ba7b6e18 100644
--- a/src/test/java/fr/igred/omero/repository/ImageTest.java
+++ b/src/test/java/fr/igred/omero/repository/ImageTest.java
@@ -61,6 +61,18 @@
 public class ImageTest extends UserTest {
 
 
+    @Test
+    public void testGetDatasets() throws Exception {
+        assertEquals(DATASET1.id, client.getImage(IMAGE1.id).getDatasets(client).get(0).getId());
+    }
+
+
+    @Test
+    public void testGetProjects() throws Exception {
+        assertEquals(PROJECT1.id, client.getImage(IMAGE1.id).getProjects(client).get(0).getId());
+    }
+
+
     @Test
     public void testImportImage() throws Exception {
         String filename1 = "8bit-unsigned&pixelType=uint8&sizeZ=5&sizeC=5&sizeT=7&sizeX=512&sizeY=512.fake";
@@ -790,7 +802,7 @@ public void testImportAndRenameImages() throws Exception {
         image2.addMapAnnotation(client, image1.getMapAnnotations(client).get(0));
 
         final RectangleWrapper rectangle = new RectangleWrapper(30, 30, 20, 20);
-        ROIWrapper roi = new ROIWrapper();
+        ROIWrapper             roi       = new ROIWrapper();
         roi.setImage(image2);
         roi.addShape(rectangle);
         image2.saveROI(client, roi);