ilgrosso commented on a change in pull request #189:
URL: https://github.com/apache/syncope/pull/189#discussion_r427782240



##########
File path: 
common/am/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/wa/GoogleMfaAuthTokenService.java
##########
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.syncope.common.rest.api.service.wa;
+
+import io.swagger.v3.oas.annotations.headers.Header;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
+import io.swagger.v3.oas.annotations.security.SecurityRequirement;
+import io.swagger.v3.oas.annotations.security.SecurityRequirements;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.apache.syncope.common.lib.to.GoogleMfaAuthTokenTO;
+import org.apache.syncope.common.rest.api.RESTHeaders;
+import org.apache.syncope.common.rest.api.service.JAXRSService;
+
+import javax.validation.constraints.NotNull;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import java.util.Date;
+import java.util.List;
+
+@Tag(name = "Google MFA Tokens")
+@SecurityRequirements({
+    @SecurityRequirement(name = "BasicAuthentication"),
+    @SecurityRequirement(name = "Bearer")})
+@Path("wa/gauth")
+public interface GoogleMfaAuthTokenService extends JAXRSService {
+
+    @DELETE
+    @Consumes({MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, 
MediaType.APPLICATION_XML})
+    @Produces({MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, 
MediaType.APPLICATION_XML})
+    @Path("tokens")
+    Response deleteTokensByDate(@NotNull @QueryParam("expirationDate") Date 
expirationDate);
+
+    @DELETE
+    @Consumes({MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, 
MediaType.APPLICATION_XML})
+    @Produces({MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, 
MediaType.APPLICATION_XML})
+    @Path("tokens/${owner}/${token}")
+    Response deleteToken(@NotNull @PathParam("owner") String owner, @NotNull 
@PathParam("token") Integer token);
+
+    @DELETE
+    @Consumes({MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, 
MediaType.APPLICATION_XML})
+    @Produces({MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, 
MediaType.APPLICATION_XML})
+    @Path("tokens/owners/${owner}")
+    Response deleteTokensFor(@NotNull @PathParam("owner") String owner);
+
+    @DELETE
+    @Consumes({MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, 
MediaType.APPLICATION_XML})
+    @Produces({MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, 
MediaType.APPLICATION_XML})
+    @Path("tokens/${token}")
+    Response deleteToken(@NotNull @PathParam("token") Integer token);
+
+    @DELETE
+    @Consumes({MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, 
MediaType.APPLICATION_XML})
+    @Produces({MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, 
MediaType.APPLICATION_XML})
+    @Path("tokens")
+    Response deleteTokens();
+
+    @ApiResponses({
+        @ApiResponse(responseCode = "201",
+            description = "GoogleMfaAuthTokenTO successfully created", headers 
= {
+            @Header(name = RESTHeaders.RESOURCE_KEY, schema =
+            @Schema(type = "string"),
+                description = "UUID generated for the entity created")})})
+    @POST
+    @Consumes({MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, 
MediaType.APPLICATION_XML})
+    @Produces({MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, 
MediaType.APPLICATION_XML})
+    @Path("tokens")
+    Response save(@NotNull GoogleMfaAuthTokenTO tokenTO);
+
+    @GET
+    @Consumes({MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, 
MediaType.APPLICATION_XML})
+    @Produces({MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, 
MediaType.APPLICATION_XML})
+    @Path("tokens/${owner}/${token}")
+    GoogleMfaAuthTokenTO findTokenFor(@NotNull @PathParam("owner") String 
owner,
+                                      @NotNull @PathParam("token") Integer 
token);
+
+    @GET
+    @Consumes({MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, 
MediaType.APPLICATION_XML})
+    @Produces({MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, 
MediaType.APPLICATION_XML})
+    @Path("tokens/owners/${owner}")
+    List<GoogleMfaAuthTokenTO> findTokensFor(@NotNull @PathParam("owner") 
String owner);
+
+    @GET
+    @Path("tokens/{key}")
+    @Consumes({MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, 
MediaType.APPLICATION_XML})
+    @Produces({MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, 
MediaType.APPLICATION_XML})
+    GoogleMfaAuthTokenTO findTokenFor(@NotNull @PathParam("key") String key);
+
+    @GET
+    @Consumes({MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, 
MediaType.APPLICATION_XML})
+    @Produces({MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, 
MediaType.APPLICATION_XML})
+    @Path("tokens/${owner}/count")
+    long countTokensForUser(@NotNull @PathParam("owner") String owner);

Review comment:
       Shouldn't this be `countTokensForOwner()` or just `countTokens(String 
owner)`

##########
File path: 
core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/auth/JPAGoogleMfaAuthToken.java
##########
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.jpa.entity.auth;
+
+import org.apache.syncope.core.persistence.api.entity.auth.GoogleMfaAuthToken;
+import 
org.apache.syncope.core.persistence.jpa.entity.AbstractGeneratedKeyEntity;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+
+import java.util.Date;
+import java.util.Optional;
+
+@Entity
+@Table(name = JPAGoogleMfaAuthToken.TABLE)
+public class JPAGoogleMfaAuthToken extends AbstractGeneratedKeyEntity 
implements GoogleMfaAuthToken {
+
+    public static final String TABLE = "GoogleMfaAuthToken";
+
+    private static final long serialVersionUID = 57352617217394093L;
+
+    @Column(nullable = false)
+    private Integer token;
+
+    @Column(nullable = false)
+    private String owner;
+
+    @Column(nullable = false)
+    private Date issuedDate;
+
+    @Override
+    public String getOwner() {
+        return owner;
+    }
+
+    @Override
+    public void setOwner(final String user) {

Review comment:
       `user` -> `owner`

##########
File path: 
core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/GoogleMfaAuthToken.java
##########
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.api.entity.auth;
+
+import org.apache.syncope.core.persistence.api.entity.Entity;
+
+import java.util.Date;
+
+public interface GoogleMfaAuthToken extends Entity {
+    String getOwner();
+
+    void setOwner(String user);

Review comment:
       `user` -> `owner`

##########
File path: 
core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/auth/GoogleMfaAuthToken.java
##########
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.api.entity.auth;
+
+import org.apache.syncope.core.persistence.api.entity.Entity;
+
+import java.util.Date;
+
+public interface GoogleMfaAuthToken extends Entity {
+    String getOwner();
+
+    void setOwner(String user);
+
+    Date getIssuedDate();
+
+    void setIssuedDate(Date user);

Review comment:
       `user` -> `issueDate`

##########
File path: 
wa/starter/src/test/java/org/apache/syncope/wa/starter/SyncopeCoreTestingServer.java
##########
@@ -68,7 +80,81 @@ public void onApplicationEvent(final ContextRefreshedEvent 
event) {
         }
     }
 
-    public class StubWAClientAppService implements WAClientAppService {
+    public static class StubGoogleMfaAuthTokenService implements 
GoogleMfaAuthTokenService {
+        private final List<GoogleMfaAuthTokenTO> tokens = new ArrayList<>();
+
+        @Override
+        public Response deleteTokensByDate(@NotNull final Date expirationDate) 
{
+            tokens.removeIf(token -> 
token.getIssuedDate().compareTo(expirationDate) >= 0);
+            return Response.noContent().build();
+        }
+
+        @Override
+        public Response deleteToken(@NotNull final String user, @NotNull final 
Integer token) {

Review comment:
       `user` -> `owner`

##########
File path: 
core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/GoogleMfaAuthTokenTest.java
##########
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.jpa.inner;
+
+import org.apache.syncope.core.persistence.api.dao.auth.GoogleMfaAuthTokenDAO;
+import org.apache.syncope.core.persistence.api.entity.auth.GoogleMfaAuthToken;
+import org.apache.syncope.core.persistence.jpa.AbstractTest;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.util.Date;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+@Transactional("Master")
+public class GoogleMfaAuthTokenTest extends AbstractTest {
+
+    @Autowired
+    private GoogleMfaAuthTokenDAO googleMfaAuthTokenDAO;
+
+    @BeforeEach
+    public void setup() {
+        googleMfaAuthTokenDAO.deleteAll();
+    }
+
+    @Test
+    public void save() {
+        create("SyncopeCreate", 123456);
+    }
+
+    @Test
+    public void count() {
+        create("SyncopeCount", 123456);
+        assertEquals(1, googleMfaAuthTokenDAO.count());
+        assertEquals(1, googleMfaAuthTokenDAO.count("SyncopeCount"));
+    }
+
+    @Test
+    public void deleteByToken() {
+        GoogleMfaAuthToken token = create("SyncopeDelete", 123456);
+        googleMfaAuthTokenDAO.delete(token.getToken());
+        assertNull(googleMfaAuthTokenDAO.find(token.getOwner(), 
token.getToken()));
+    }
+
+    @Test
+    public void deleteByUser() {

Review comment:
       `deleteByOwner()`

##########
File path: 
wa/starter/src/test/java/org/apache/syncope/wa/starter/SyncopeCoreTestingServer.java
##########
@@ -68,7 +80,81 @@ public void onApplicationEvent(final ContextRefreshedEvent 
event) {
         }
     }
 
-    public class StubWAClientAppService implements WAClientAppService {
+    public static class StubGoogleMfaAuthTokenService implements 
GoogleMfaAuthTokenService {
+        private final List<GoogleMfaAuthTokenTO> tokens = new ArrayList<>();
+
+        @Override
+        public Response deleteTokensByDate(@NotNull final Date expirationDate) 
{
+            tokens.removeIf(token -> 
token.getIssuedDate().compareTo(expirationDate) >= 0);
+            return Response.noContent().build();
+        }
+
+        @Override
+        public Response deleteToken(@NotNull final String user, @NotNull final 
Integer token) {
+            tokens.removeIf(to -> to.getToken().equals(token) && 
to.getOwner().equalsIgnoreCase(user));
+            return Response.noContent().build();
+        }
+
+        @Override
+        public Response deleteTokensFor(@NotNull final String owner) {
+            tokens.removeIf(to -> to.getOwner().equalsIgnoreCase(owner));
+            return Response.noContent().build();
+        }
+
+        @Override
+        public Response deleteToken(@NotNull final Integer token) {
+            tokens.removeIf(to -> to.getToken().equals(token));
+            return Response.noContent().build();
+        }
+
+        @Override
+        public Response deleteTokens() {
+            tokens.clear();
+            return Response.noContent().build();
+        }
+
+        @Override
+        public Response save(@NotNull final GoogleMfaAuthTokenTO tokenTO) {
+            tokenTO.setKey(UUID.randomUUID().toString());
+            tokens.add(tokenTO);
+            return Response.ok().build();
+        }
+
+        @Override
+        public GoogleMfaAuthTokenTO findTokenFor(@NotNull final String user, 
@NotNull final Integer token) {
+            return tokens.stream()
+                .filter(to -> to.getToken().equals(token) && 
to.getOwner().equalsIgnoreCase(user))
+                .findFirst().get();
+        }
+
+        @Override
+        public List<GoogleMfaAuthTokenTO> findTokensFor(@NotNull final String 
user) {
+            return tokens.stream()
+                .filter(to -> to.getOwner().equalsIgnoreCase(user))
+                .collect(Collectors.toList());
+        }
+
+        @Override
+        public GoogleMfaAuthTokenTO findTokenFor(@NotNull final String key) {
+            return tokens.stream()
+                .filter(to -> to.getKey().equalsIgnoreCase(key))
+                .findFirst().get();
+        }
+
+        @Override
+        public long countTokensForUser(@NotNull final String user) {

Review comment:
       `user` -> `owner`

##########
File path: 
core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/auth/JPAGoogleMfaAuthTokenDAO.java
##########
@@ -0,0 +1,140 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.syncope.core.persistence.jpa.dao.auth;
+
+import org.apache.syncope.core.persistence.api.dao.auth.GoogleMfaAuthTokenDAO;
+import org.apache.syncope.core.persistence.api.entity.auth.GoogleMfaAuthToken;
+import org.apache.syncope.core.persistence.jpa.dao.AbstractDAO;
+import 
org.apache.syncope.core.persistence.jpa.entity.auth.JPAGoogleMfaAuthToken;
+import org.springframework.stereotype.Repository;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.persistence.NoResultException;
+import javax.persistence.TypedQuery;
+
+import java.util.Date;
+import java.util.List;
+
+@Repository
+public class JPAGoogleMfaAuthTokenDAO extends AbstractDAO<GoogleMfaAuthToken> 
implements GoogleMfaAuthTokenDAO {
+    @Override
+    @Transactional(readOnly = true)
+    public GoogleMfaAuthToken find(final String key) {
+        return entityManager().find(JPAGoogleMfaAuthToken.class, key);
+    }
+
+    @Override
+    @Transactional(readOnly = true)
+    public GoogleMfaAuthToken find(final String owner, final Integer otp) {
+        TypedQuery<GoogleMfaAuthToken> query = entityManager().createQuery(
+            "SELECT e FROM " + JPAGoogleMfaAuthToken.class.getSimpleName()
+                + " e WHERE e.owner=:owner AND e.token=:token",
+            GoogleMfaAuthToken.class);
+        query.setParameter("owner", owner);
+        query.setParameter("token", otp);
+        GoogleMfaAuthToken result = null;
+        try {
+            result = query.getSingleResult();
+        } catch (final NoResultException e) {
+            LOG.debug("No Google Mfa Token found for owner = {} and otp = {}", 
owner, otp);
+        }
+        return result;
+    }
+
+    @Override
+    public GoogleMfaAuthToken save(final GoogleMfaAuthToken token) {
+        return entityManager().merge(token);
+    }
+
+    @Override
+    @Transactional(readOnly = true)
+    public long count(final String owner) {
+        TypedQuery<Long> query = entityManager().createQuery(
+            "SELECT COUNT(e.user) FROM " + 
JPAGoogleMfaAuthToken.class.getSimpleName() + " e WHERE e.owner=:owner",

Review comment:
       `e.owner`

##########
File path: 
core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/auth/JPAGoogleMfaAuthTokenDAO.java
##########
@@ -0,0 +1,140 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.syncope.core.persistence.jpa.dao.auth;
+
+import org.apache.syncope.core.persistence.api.dao.auth.GoogleMfaAuthTokenDAO;
+import org.apache.syncope.core.persistence.api.entity.auth.GoogleMfaAuthToken;
+import org.apache.syncope.core.persistence.jpa.dao.AbstractDAO;
+import 
org.apache.syncope.core.persistence.jpa.entity.auth.JPAGoogleMfaAuthToken;
+import org.springframework.stereotype.Repository;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.persistence.NoResultException;
+import javax.persistence.TypedQuery;
+
+import java.util.Date;
+import java.util.List;
+
+@Repository
+public class JPAGoogleMfaAuthTokenDAO extends AbstractDAO<GoogleMfaAuthToken> 
implements GoogleMfaAuthTokenDAO {
+    @Override
+    @Transactional(readOnly = true)
+    public GoogleMfaAuthToken find(final String key) {
+        return entityManager().find(JPAGoogleMfaAuthToken.class, key);
+    }
+
+    @Override
+    @Transactional(readOnly = true)
+    public GoogleMfaAuthToken find(final String owner, final Integer otp) {
+        TypedQuery<GoogleMfaAuthToken> query = entityManager().createQuery(
+            "SELECT e FROM " + JPAGoogleMfaAuthToken.class.getSimpleName()
+                + " e WHERE e.owner=:owner AND e.token=:token",
+            GoogleMfaAuthToken.class);
+        query.setParameter("owner", owner);
+        query.setParameter("token", otp);
+        GoogleMfaAuthToken result = null;
+        try {
+            result = query.getSingleResult();
+        } catch (final NoResultException e) {
+            LOG.debug("No Google Mfa Token found for owner = {} and otp = {}", 
owner, otp);
+        }
+        return result;
+    }
+
+    @Override
+    public GoogleMfaAuthToken save(final GoogleMfaAuthToken token) {
+        return entityManager().merge(token);
+    }
+
+    @Override
+    @Transactional(readOnly = true)
+    public long count(final String owner) {
+        TypedQuery<Long> query = entityManager().createQuery(
+            "SELECT COUNT(e.user) FROM " + 
JPAGoogleMfaAuthToken.class.getSimpleName() + " e WHERE e.owner=:owner",
+            Long.class);
+        query.setParameter("owner", owner);
+        return query.getSingleResult();
+    }
+
+    @Override
+    @Transactional(readOnly = true)
+    public long count() {
+        TypedQuery<Long> query = entityManager().createQuery(
+            "SELECT COUNT(e.owner) FROM " + 
JPAGoogleMfaAuthToken.class.getSimpleName() + " e",
+            Long.class);
+        return query.getSingleResult();
+    }
+
+    @Override
+    public void deleteAll() {
+        entityManager()
+            .createQuery("DELETE FROM " + 
JPAGoogleMfaAuthToken.class.getSimpleName() + " e")
+            .executeUpdate();
+    }
+
+    @Override
+    public void delete(final Integer otp) {
+        entityManager()
+            .createQuery("DELETE FROM " + 
JPAGoogleMfaAuthToken.class.getSimpleName() + " e WHERE e.token=:token")
+            .setParameter("token", otp)
+            .executeUpdate();
+    }
+
+    @Override
+    public void delete(final String owner) {
+        entityManager()
+            .createQuery("DELETE FROM " + 
JPAGoogleMfaAuthToken.class.getSimpleName() + " e WHERE e.owner=:owner")
+            .setParameter("owner", owner)
+            .executeUpdate();
+    }
+
+    @Override
+    public void delete(final String owner, final Integer otp) {
+        entityManager()
+            .createQuery("DELETE FROM " + 
JPAGoogleMfaAuthToken.class.getSimpleName()
+                + " e WHERE e.owner=:user AND e.token=:token")
+            .setParameter("owner", owner)
+            .setParameter("token", otp)
+            .executeUpdate();
+    }
+
+    @Override
+    public void delete(final Date expirationDate) {
+        entityManager()
+            .createQuery("DELETE FROM " + 
JPAGoogleMfaAuthToken.class.getSimpleName()
+                + " e WHERE e.issuedDateTime>=:expired")
+            .setParameter("expired", expirationDate)
+            .executeUpdate();
+    }
+
+    @Override
+    public List<GoogleMfaAuthToken> findForOwner(final String owner) {
+        TypedQuery<GoogleMfaAuthToken> query = entityManager().createQuery(
+            "SELECT e FROM " + JPAGoogleMfaAuthToken.class.getSimpleName() + " 
e WHERE e.owner=:owner",
+            GoogleMfaAuthToken.class);
+        query.setParameter("owner", owner);
+        try {
+            return query.getResultList();
+        } catch (final NoResultException e) {
+            LOG.debug("No Google Mfa Token found for user = {}", owner);

Review comment:
       `"No Google Mfa Token found for owner = {}"`

##########
File path: 
core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/auth/JPAGoogleMfaAuthTokenDAO.java
##########
@@ -0,0 +1,140 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.syncope.core.persistence.jpa.dao.auth;
+
+import org.apache.syncope.core.persistence.api.dao.auth.GoogleMfaAuthTokenDAO;
+import org.apache.syncope.core.persistence.api.entity.auth.GoogleMfaAuthToken;
+import org.apache.syncope.core.persistence.jpa.dao.AbstractDAO;
+import 
org.apache.syncope.core.persistence.jpa.entity.auth.JPAGoogleMfaAuthToken;
+import org.springframework.stereotype.Repository;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.persistence.NoResultException;
+import javax.persistence.TypedQuery;
+
+import java.util.Date;
+import java.util.List;
+
+@Repository
+public class JPAGoogleMfaAuthTokenDAO extends AbstractDAO<GoogleMfaAuthToken> 
implements GoogleMfaAuthTokenDAO {
+    @Override
+    @Transactional(readOnly = true)
+    public GoogleMfaAuthToken find(final String key) {
+        return entityManager().find(JPAGoogleMfaAuthToken.class, key);
+    }
+
+    @Override
+    @Transactional(readOnly = true)
+    public GoogleMfaAuthToken find(final String owner, final Integer otp) {
+        TypedQuery<GoogleMfaAuthToken> query = entityManager().createQuery(
+            "SELECT e FROM " + JPAGoogleMfaAuthToken.class.getSimpleName()
+                + " e WHERE e.owner=:owner AND e.token=:token",
+            GoogleMfaAuthToken.class);
+        query.setParameter("owner", owner);
+        query.setParameter("token", otp);
+        GoogleMfaAuthToken result = null;
+        try {
+            result = query.getSingleResult();
+        } catch (final NoResultException e) {
+            LOG.debug("No Google Mfa Token found for owner = {} and otp = {}", 
owner, otp);
+        }
+        return result;
+    }
+
+    @Override
+    public GoogleMfaAuthToken save(final GoogleMfaAuthToken token) {
+        return entityManager().merge(token);
+    }
+
+    @Override
+    @Transactional(readOnly = true)
+    public long count(final String owner) {
+        TypedQuery<Long> query = entityManager().createQuery(
+            "SELECT COUNT(e.user) FROM " + 
JPAGoogleMfaAuthToken.class.getSimpleName() + " e WHERE e.owner=:owner",
+            Long.class);
+        query.setParameter("owner", owner);
+        return query.getSingleResult();
+    }
+
+    @Override
+    @Transactional(readOnly = true)
+    public long count() {
+        TypedQuery<Long> query = entityManager().createQuery(
+            "SELECT COUNT(e.owner) FROM " + 
JPAGoogleMfaAuthToken.class.getSimpleName() + " e",
+            Long.class);
+        return query.getSingleResult();
+    }
+
+    @Override
+    public void deleteAll() {
+        entityManager()
+            .createQuery("DELETE FROM " + 
JPAGoogleMfaAuthToken.class.getSimpleName() + " e")
+            .executeUpdate();
+    }
+
+    @Override
+    public void delete(final Integer otp) {
+        entityManager()
+            .createQuery("DELETE FROM " + 
JPAGoogleMfaAuthToken.class.getSimpleName() + " e WHERE e.token=:token")
+            .setParameter("token", otp)
+            .executeUpdate();
+    }
+
+    @Override
+    public void delete(final String owner) {
+        entityManager()
+            .createQuery("DELETE FROM " + 
JPAGoogleMfaAuthToken.class.getSimpleName() + " e WHERE e.owner=:owner")
+            .setParameter("owner", owner)
+            .executeUpdate();
+    }
+
+    @Override
+    public void delete(final String owner, final Integer otp) {
+        entityManager()
+            .createQuery("DELETE FROM " + 
JPAGoogleMfaAuthToken.class.getSimpleName()
+                + " e WHERE e.owner=:user AND e.token=:token")

Review comment:
       `:owner`

##########
File path: 
wa/starter/src/test/java/org/apache/syncope/wa/starter/SyncopeCoreTestingServer.java
##########
@@ -68,7 +80,81 @@ public void onApplicationEvent(final ContextRefreshedEvent 
event) {
         }
     }
 
-    public class StubWAClientAppService implements WAClientAppService {
+    public static class StubGoogleMfaAuthTokenService implements 
GoogleMfaAuthTokenService {
+        private final List<GoogleMfaAuthTokenTO> tokens = new ArrayList<>();
+
+        @Override
+        public Response deleteTokensByDate(@NotNull final Date expirationDate) 
{
+            tokens.removeIf(token -> 
token.getIssuedDate().compareTo(expirationDate) >= 0);
+            return Response.noContent().build();
+        }
+
+        @Override
+        public Response deleteToken(@NotNull final String user, @NotNull final 
Integer token) {
+            tokens.removeIf(to -> to.getToken().equals(token) && 
to.getOwner().equalsIgnoreCase(user));
+            return Response.noContent().build();
+        }
+
+        @Override
+        public Response deleteTokensFor(@NotNull final String owner) {
+            tokens.removeIf(to -> to.getOwner().equalsIgnoreCase(owner));
+            return Response.noContent().build();
+        }
+
+        @Override
+        public Response deleteToken(@NotNull final Integer token) {
+            tokens.removeIf(to -> to.getToken().equals(token));
+            return Response.noContent().build();
+        }
+
+        @Override
+        public Response deleteTokens() {
+            tokens.clear();
+            return Response.noContent().build();
+        }
+
+        @Override
+        public Response save(@NotNull final GoogleMfaAuthTokenTO tokenTO) {
+            tokenTO.setKey(UUID.randomUUID().toString());
+            tokens.add(tokenTO);
+            return Response.ok().build();
+        }
+
+        @Override
+        public GoogleMfaAuthTokenTO findTokenFor(@NotNull final String user, 
@NotNull final Integer token) {

Review comment:
       `user` -> `owner`

##########
File path: 
core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/GoogleMfaAuthTokenTest.java
##########
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.jpa.inner;
+
+import org.apache.syncope.core.persistence.api.dao.auth.GoogleMfaAuthTokenDAO;
+import org.apache.syncope.core.persistence.api.entity.auth.GoogleMfaAuthToken;
+import org.apache.syncope.core.persistence.jpa.AbstractTest;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.util.Date;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+@Transactional("Master")
+public class GoogleMfaAuthTokenTest extends AbstractTest {
+
+    @Autowired
+    private GoogleMfaAuthTokenDAO googleMfaAuthTokenDAO;
+
+    @BeforeEach
+    public void setup() {
+        googleMfaAuthTokenDAO.deleteAll();
+    }
+
+    @Test
+    public void save() {
+        create("SyncopeCreate", 123456);
+    }
+
+    @Test
+    public void count() {
+        create("SyncopeCount", 123456);
+        assertEquals(1, googleMfaAuthTokenDAO.count());
+        assertEquals(1, googleMfaAuthTokenDAO.count("SyncopeCount"));
+    }
+
+    @Test
+    public void deleteByToken() {
+        GoogleMfaAuthToken token = create("SyncopeDelete", 123456);
+        googleMfaAuthTokenDAO.delete(token.getToken());
+        assertNull(googleMfaAuthTokenDAO.find(token.getOwner(), 
token.getToken()));
+    }
+
+    @Test
+    public void deleteByUser() {
+        GoogleMfaAuthToken token = create("SyncopeDelete", 123456);
+        googleMfaAuthTokenDAO.delete(token.getOwner());
+        assertNull(googleMfaAuthTokenDAO.find(token.getOwner(), 
token.getToken()));
+    }
+
+    @Test
+    public void deleteByUserAndToken() {

Review comment:
       `deleteByOwnerAndToken()`




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
us...@infra.apache.org


Reply via email to