Expected to find class in file - sonata-admin

I'm following the doc about the installation of Sonata Admin Bundle. I'm havong a problem during the step 'Creating an Admin'.
I'm Having the following error :
Expected to find class "AppBundle\Admin\CategoryAdmin" in file
"[...]/src/AppBundle/Admin/CategoryAdmin.php"
while importing services from resource "../../src/AppBundle/*", but it
was not found! Check the namespace prefix used with the resource in
[...]/app/config/services.yml
(which is being imported from
"[...]/app/config/config.yml").
I've created my "Category Admin class"
<?php
// src/Admin/CategoryAdmin.php
namespace App\Admin;
use Sonata\AdminBundle\Admin\AbstractAdmin;
use Sonata\AdminBundle\Datagrid\ListMapper;
use Sonata\AdminBundle\Datagrid\DatagridMapper;
use Sonata\AdminBundle\Form\FormMapper;
use Symfony\Component\Form\Extension\Core\Type\TextType;
class CategoryAdmin extends AbstractAdmin
{
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper->add('name', TextType::class);
}
protected function configureDatagridFilters(DatagridMapper $datagridMapper)
{
$datagridMapper->add('name');
}
protected function configureListFields(ListMapper $listMapper)
{
$listMapper->addIdentifier('name');
}
}
and set them into the config.yml
imports:
- { resource: parameters.yml }
- { resource: security.yml }
- { resource: services.yml }
# Put parameters here that don't need to change on each machine where the app is deployed
# https://symfony.com/doc/current/best_practices/configuration.html#application-related-configuration
parameters:
locale: fr
framework:
#esi: ~
translator: { fallbacks: ['%locale%'] }
secret: '%secret%'
templating:
engines: ['twig']
router:
resource: '%kernel.project_dir%/app/config/routing.yml'
strict_requirements: ~
form: ~
csrf_protection: ~
validation: { enable_annotations: true }
#serializer: { enable_annotations: true }
default_locale: '%locale%'
trusted_hosts: ~
session:
# https://symfony.com/doc/current/reference/configuration/framework.html#handler-id
handler_id: session.handler.native_file
save_path: '%kernel.project_dir%/var/sessions/%kernel.environment%'
fragments: ~
http_method_override: true
assets: ~
php_errors:
log: true
services:
# ...
admin.category:
class: App\Admin\CategoryAdmin
arguments: [~, App\Entity\Category, ~]
tags:
- { name: sonata.admin, manager_type: orm, label: Category }
public: true
# Twig Configuration
twig:
debug: '%kernel.debug%'
strict_variables: '%kernel.debug%'
# Doctrine Configuration
doctrine:
dbal:
driver: pdo_mysql
host: '%database_host%'
port: '%database_port%'
dbname: '%database_name%'
user: '%database_user%'
password: '%database_password%'
charset: UTF8
# if using pdo_sqlite as your database driver:
# 1. add the path in parameters.yml
# e.g. database_path: '%kernel.project_dir%/var/data/data.sqlite'
# 2. Uncomment database_path in parameters.yml.dist
# 3. Uncomment next line:
#path: '%database_path%'
orm:
auto_generate_proxy_classes: '%kernel.debug%'
naming_strategy: doctrine.orm.naming_strategy.underscore
auto_mapping: true
# Swiftmailer Configuration
swiftmailer:
transport: '%mailer_transport%'
host: '%mailer_host%'
username: '%mailer_user%'
password: '%mailer_password%'
spool: { type: memory }
fos_user:
db_driver: orm # other valid values are 'mongodb' and 'couchdb'
firewall_name: main
user_class: AppBundle\Entity\User
from_email:
address: "%mailer_user%"
sender_name: "%mailer_user%"
# config/packages/sonata_admin.yaml
sonata_block:
blocks:
# enable the SonataAdminBundle block
sonata.admin.block.admin_list:
contexts: [admin]
THe route is working so I will not post it.
My project is built like you see under the screenshot :
Symfony Structure
So Is there a problem with how I've built my project or I'm doing something on the config files ?
I've tried to copy/paste files names in case the problem is from a file name but It change anything. Any idea ?

Your namespace is incorrect as it seems from error and first look.
Check following code.
<?php
namespace AppBundle\Admin; # CHANGE THIS LINE TO FOLLOWING!
use Sonata\AdminBundle\Admin\AbstractAdmin;
use Sonata\AdminBundle\Datagrid\ListMapper;
use Sonata\AdminBundle\Datagrid\DatagridMapper;
use Sonata\AdminBundle\Form\FormMapper;
use Symfony\Component\Form\Extension\Core\Type\TextType;
class CategoryAdmin extends AbstractAdmin
{
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper->add('name', TextType::class);
}
protected function configureDatagridFilters(DatagridMapper $datagridMapper)
{
$datagridMapper->add('name');
}
protected function configureListFields(ListMapper $listMapper)
{
$listMapper->addIdentifier('name');
}
}
And services
services:
# ...
admin.category:
class: AppBundle\Admin\CategoryAdmin # fix here
arguments: [~, AppBundle\Entity\Category, ~] # and here
tags:
- { name: sonata.admin, manager_type: orm, label: Category }
public: true

Related

How to configure NGINX with websocket for React app hosted in Kubernetes

The websocket (using #stomp/stompjs library) connection works ok on local development bench. Running the app on Azure AKS platform using an NGINX proxy for serving the React app (with also NGINX ingress controller) also works fine. The challenge, however, is the websocket connection in the latter environment.
Firefox browser returns the following error when trying to connect:
Uncaught (in promise) DOMException: An invalid or illegal string was specified ...
i stomp-handler.ts:31
(Async: promise callback)
s stomp-handler.ts:31
da stomp-handler.ts:31
da stomp-handler.ts:31
value client.ts:404
value client.ts:401
In React, the websocket configuration is:
const server = {
name: process.env.REACT_APP_BASE_URL_SOCKET,
}
...
const client = new Client();
client.configure({
brokerURL: server.name,
...
The environment variable is:
REACT_APP_BASE_URL_SOCKET=/ws
NGINX configuration for React app is specified as:
...
http {
...
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
# listen on port 80
listen 80;
server_name foo.com;
gzip off;
proxy_max_temp_file_size 0;
# save logs here
access_log /var/log/nginx/access.log compression;
root /usr/share/nginx/html;
index index.html index.htm;
proxy_set_header HOST $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
location / {
try_files $uri $uri/ /index.html =404;
}
location /ws {
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_pass http://bar-api-service.default.svc.cluster.local:8000;
}
...
}
}
With Docker file as:
FROM nginx:alpine
COPY build/ /usr/share/nginx/html
# Copy our custom nginx config
COPY nginx.conf /etc/nginx/nginx.conf
EXPOSE 80
ENTRYPOINT ["nginx", "-g", "daemon off;"]
Meanwhile, the Kubernetes NGINX Ingress controller is configured as:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: foo-ingress
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/rewrite-target: /
cert-manager.io/cluster-issuer: letsencrypt
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
nginx.ingress.kubernetes.io/secure-backends: "true"
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/websocket-services: bar-ui-service
nginx.org/websocket-services: bar-ui-service
spec:
tls:
- hosts:
- foo.com
secretName: tls-secret
rules:
- host: foo.com
http:
paths:
- pathType: Prefix
backend:
service:
name: bar-ui-service
port:
number: 80
path: /
With "bar-ui-service" as:
apiVersion: v1
kind: Service
metadata:
name: bar-ui-service
spec:
type: NodePort
ports:
- name: http
protocol: TCP
port: 80
targetPort: 80
selector:
app: bar-ui
And "bar-api-service" as:
apiVersion: v1
kind: Service
metadata:
name: bar-api-service
spec:
selector:
app: bar-api
tier: backend
ports:
port: 8000
targetPort: 8000
In the Spring Boot API serving the websocket, Spring Security is used, with OAuth2 resource server. Configuration of HttpSecurity is:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic().disable()
.formLogin().disable()
.and()
.cors()
.and()
.authorizeRequests()
.antMatchers("/ws").permitAll()
And websocket broker config as:
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketBrokerConfig implements WebSocketMessageBrokerConfigurer {
#Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/queue", "/topic");
registry.setApplicationDestinationPrefixes("/app");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws")
.setAllowedOrigins("foo.com");
}
}
How do I get the websocket to work in k8s?
To solve this challenge in Kubernetes, I had to change the WebSocket library. Simply put, the '#stomp/stompjs' library (v6.1.2) cannot upgrade from http to ws. After trying out several libraries (yes, JavaScript WebSocket libraries seem to be a dime a dozen out there!), I settled on react-stomp-hooks (v2.1.0).
For those who might be curious about the fuller solution, read on.
In React (TS), the 'react-stomp-hook' library comes with a convenient Provider component for connecting to the server. This may be added to App.tsx like so:
...
import {
StompSessionProvider,
} from "react-stomp-hooks";
import WebSocketSubscriptions from './shared/WebSocketSubscription';
...
const socketHeaders = {
login: token.userId,
passcode: <can be anything>
}
return (
<React.Fragment>
{token &&
<StompSessionProvider
url={server.name}
connectHeaders={socketHeaders}>
<WebSocketSubscriptions />
</StompSessionProvider>}
...
The url ('server.name') is from an environment variable defined as:
REACT_APP_BASE_URL_SOCKET=/ws
for Kubernetes deployment, and:
REACT_APP_BASE_URL_SOCKET=http://localhost:8000/ws
in the local development environment.
In order to work, the web socket's connection header requires to pass down a user identifier to match with Spring Security's user authentication (shown below). In my example, this is provided by the OAuth 2.0 JWT token. However, the "passcode", although a required parameter, can be anything. Note the Provider enables subscriptions to be defined in a separate component (or components) using a useSubscription hook, for example:
import {
useSubscription,
} from "react-stomp-hooks";
const WebSocketSubscriptions: React.FC = () => {
useSubscription("/app/subscribe", (message) => {
console.log(`..subscribed ${message}`);
});
...
The React-serving NGINX proxy configuration is as follows (you can see the fuller implementation in my question above):
...
location /ws {
proxy_pass http://bar-api-service.default.svc.cluster.local:8000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_cache_bypass $http_upgrade;
}
...
Meanwhile, the Kubernetes NGINX ingress controller need only deal with secure termination of traffic, like:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: foo-ingress
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/rewrite-target: /
cert-manager.io/cluster-issuer: letsencrypt
spec:
tls:
- hosts:
- foo.com
secretName: tls-secret
rules:
- host: foo.com
...
The heavy-lifting now comes in the Spring Boot (v2.7.3) API where the WebSocket server resides. Addition of Spring Security is what makes the solution hairy.
At least the following dependencies are required:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-messaging</artifactId>
<version>5.6.2</version>
</dependency>
First let's look at the websocket's support files:
The AuthChannelInterceptorAdaptor class authenticates the user:
#Component
public class AuthChannelInterceptorAdaptor implements ChannelInterceptor {
private static final String USERNAME_HEADER = "login";
private static final String PASSWORD_HEADER = "passcode";
private final WebSocketAuthenticatorService webSocketAuthenticatorService;
#Autowired
public AuthChannelInterceptorAdaptor(final WebSocketAuthenticatorService webSocketAuthenticatorService) {
this.webSocketAuthenticatorService = webSocketAuthenticatorService;
}
#Override
public Message<?> preSend(final Message<?> message, final MessageChannel channel)
throws AuthenticationException {
final StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
if (StompCommand.CONNECT == accessor.getCommand()) {
final String username = accessor.getFirstNativeHeader(USERNAME_HEADER);
final String password = accessor.getFirstNativeHeader(PASSWORD_HEADER);
final UsernamePasswordAuthenticationToken user =
webSocketAuthenticatorService.getAuthenticatedOrFail(username, password);
accessor.setUser(user);
}
return message;
}
}
As mentioned earlier, for an API server using the OAuth 2.0 Resource library, only the username need be provided; password can be anything. Spring Boot Security simply authenticates the user id (from the websocket's header login) and accepts the connection.
The authentication is carried out in the WebSocketAuthenticatorService class:
/*
courtesy: Anthony Raymond: https://stackoverflow.com/questions/45405332/websocket-authentication-and-authorization-in-spring
*/
#Component
public class WebSocketAuthenticatorService {
public UsernamePasswordAuthenticationToken getAuthenticatedOrFail(final String username,
final String password)
throws AuthenticationException {
if (username == null || username.trim().isEmpty()) {
throw new AuthenticationCredentialsNotFoundException("Username was null or empty.");
}
if (password == null || password.trim().isEmpty()) {
throw new AuthenticationCredentialsNotFoundException("Password was null or empty.");
}
// You can add your own logic for retrieving user from, say, db
//if (fetchUserFromDb(username, password) == null) {
// throw new BadCredentialsException("Bad credentials for user " + username);
// Null credentials, we do not pass the password along
return new UsernamePasswordAuthenticationToken(
username,
null,
Collections.singleton((GrantedAuthority) () -> "USER") // MUST provide at least one role
);
}
}
The WebSocketAuthorizationSecurityConfig class extends AbstractSecurityWebSocketMessageBrokerConfigurer, allowing configuration of inbound messages:
#Configuration
public class WebSocketAuthorizationSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {
#Override
protected void configureInbound(final MessageSecurityMetadataSourceRegistry messages) {
// You can customize your authorization mapping here.
messages.anyMessage().authenticated();
}
#Override
protected boolean sameOriginDisabled() {
return false;
}
}
Then we come to the WebSocketBrokerConfig class, which implements WebSocketMessageBrokerConfigurer:
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketBrokerConfig implements WebSocketMessageBrokerConfigurer {
#Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/queue", "/topic");
registry.setApplicationDestinationPrefixes("/app");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws")
.setAllowedOrigins("http://localhost:3000", "foo.com")
.withSockJS();
}
}
This in-memory broker is good for training and POCs. In production, however, a more robust provider like RabbitMQ or Kafka is called for.
Another implementation of the WebSocketMessageBrokerConfigurer is required for configuring the inbound channel. This is implemented in the class WebSocketSecurityConfig:
#Configuration
#Order(Ordered.HIGHEST_PRECEDENCE + 99)
public class WebSocketSecurityConfig implements WebSocketMessageBrokerConfigurer {
#Autowired
private AuthChannelInterceptorAdaptor authChannelInterceptorAdapter;
#Override
public void registerStompEndpoints(final StompEndpointRegistry registry) {
// Endpoints are already registered on WebSocketConfig, no need to add more.
}
#Override
public void configureClientInboundChannel(final ChannelRegistration registration) {
registration.setInterceptors(authChannelInterceptorAdapter);
}
}
Note the #Order annotation, which configures a high precedence for the channel.
The React app client move from a ws-based websocket connection to an http-brokered one also requires a CORS filter to work. The example filter looks like:
/*
courtesy of https://stackoverflow.com/users/3669624/cнŝdk
*/
#Component
public class CORSFilter implements Filter {
private final List<String> allowedOrigins = Arrays.asList(
"http://localhost:3000", "foo.com");
public void destroy() {}
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
// Lets make sure that we are working with HTTP (that is, against HttpServletRequest and HttpServletResponse objects)
if (req instanceof HttpServletRequest && res instanceof HttpServletResponse) {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
// Access-Control-Allow-Origin
String origin = request.getHeader("Origin");
response.setHeader("Access-Control-Allow-Origin", allowedOrigins.contains(origin) ? origin : "");
response.setHeader("Vary", "Origin");
// Access-Control-Max-Age
response.setHeader("Access-Control-Max-Age", "3600");
// Access-Control-Allow-Credentials
response.setHeader("Access-Control-Allow-Credentials", "true");
// Access-Control-Allow-Methods
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
// Access-Control-Allow-Headers
response.setHeader("Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept, " + "X-CSRF-TOKEN");
}
chain.doFilter(req, res);
}
public void init(FilterConfig filterConfig) {
}
}
The WebSocketController class simply provides an endpoint for the websocket's subscriptions and support for discrete '/queues'.
#Controller
public class WebSocketController {
#MessageMapping("/subscribe")
#SendToUser("/queue/notification")
public String replyToAccountFromClient(#Payload String message,
Principal user) {
return String.format("hello: %s", message);
}
#MessageExceptionHandler
#SendTo("/queue/errors")
public String handleException(Throwable exception) {
return exception.getMessage();
}
}
Finally, the HttpSecurity configuration looks like the following:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic().disable()
.formLogin().disable()
.addFilterAt(corsFilter, BasicAuthenticationFilter.class)
.csrf().ignoringAntMatchers(API_URL_PREFIX)
.and()
.cors()
.and()
.authorizeRequests()
.antMatchers("/ws/**").permitAll()
...
.oauth2ResourceServer(oauth2ResourceServer ->
oauth2ResourceServer.jwt(jwt ->
jwt.jwtAuthenticationConverter(
getJwtAuthenticationConverter())))
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}

how can i get the user details from current session in react js for datadogRUM setUser

import { datadogRum } from '#datadog/browser-rum'
datadogRum.init({
applicationId: '<application_id>',
clientToken: '<CLIENT_TOKEN>',
site: 'datadoghq.com',
service:'client-service',
// Specify a version number to identify the deployed version of your application in Datadog
// version: '1.0.0',
// env: 'staging',
// env: 'prod',
sampleRate: 100,
trackInteractions: true
});
datadogRum.setUser({
name: ????
email: ????
});
How should i access the name and email to show in datadog??
what should i replace the above ?? with can anyone help.

Google Appengine Standard : Java8 Setting up Endpoints with IAP and ESP

I am working on a web application that runs on Google Appengine and is able to be accessed from Android app that will synchronise some data from the server. I had this working smoothly until recent when I recreated the project on GCP and setup IAP, which works fine.
However, I am not able to configure the end point, where I keep getting 500 Server Error. I have followed every step in this online instructions from Google https://cloud.google.com/endpoints/docs/openapi/get-started-app-engine-standard and not able to figure out what am I doing wrong!!
Here are the contents of my files:
Echo.java
package com.example.endpoints;
import ...
#Api(
name = "echo",
version = "v1",
clientIds = { IAP_CREATED_WEBCLIENTID_FROM_CREDENTIALS, Constant.API_EXPLORER_CLIENT_ID},
audiences = {IAP_CREATED_WEBCLIENTID_FROM_CREDENTIALS},
namespace = #ApiNamespace(
ownerDomain = "com.example.endpoints",
ownerName = "com.example.endpoints",
packagePath = "")
)
public class Echo {
#ApiMethod(name = "echoonly", path = "/echo/v1/echoonly")
public Entity echoonly() {
Entity testingResponse = new Entity("TestingResponse", 1000000L);
testingResponse.setProperty("name", "Only");
return testingResponse;
}
#ApiMethod(name = "echoname", path = "/echo/v1/echoname/{name}")
public Entity echoname(
#Named("name") #Nullable String name) {
Entity testingResponse = new Entity("TestingResponse", 1000000L);
testingResponse.setProperty("name", name);
return testingResponse;
}
#ApiMethod(name = "echoage", path = "/echo/v1/echoage/{name}/{age}")
public Entity echoage(
#Named("name") String name,
#Named("age") String age) {
Entity testingResponse = new Entity("TestingResponse", 1000000L);
testingResponse.setProperty("name", name);
testingResponse.setProperty("age", age);
return testingResponse;
}
}
pom.xml (partial only which I think are relevant here...)
<groupId>endpoints</groupId>
<artifactId>endpoints</artifactId>
<properties>
<endpoints.project.id>MYCLOUDRUNSERVICENAME-HASHCODE-uc.a.run.app</endpoints.project.id>
<appengine.maven.plugin.version>2.3.0</appengine.maven.plugin.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.showDeprecation>true</maven.compiler.showDeprecation>
<endpoints.framework.version>2.2.1</endpoints.framework.version>
<endpoints.management.version>1.0.4</endpoints.management.version>
</properties>
<dependencies>...</dependencies>
<build>
<plugins>...</plugins>
</build>
appengine-web-.xml
<threadsafe>true</threadsafe>
<sessions-enabled>false</sessions-enabled>
<service>default</service> <!-- THIS IS USED TO UPLOAD TO A SPECIFIC SERVICE. REMOVE THIS FOR DEFAULT -->
<runtime>java8</runtime>
<system-properties>
<property name="java.util.logging.config.file"
value="WEB-INF/logging.properties" />
</system-properties>
<env-variables>
<env-var name="ENDPOINTS_SERVICE_NAME" value="${endpoints.project.id}" />
</env-variables>
web.xml
<servlet>
<servlet-name>EndpointsServlet</servlet-name>
<servlet-class>com.google.api.server.spi.EndpointsServlet</servlet-class>
<init-param>
<param-name>services</param-name>
<param-value>com.example.endpoints.Echo</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>EndpointsServlet</servlet-name>
<url-pattern>/_ah/api/*</url-pattern>
</servlet-mapping>
<filter>
<filter-name>endpoints-api-configuration</filter-name>
<filter-class>com.google.api.control.ServiceManagementConfigFilter</filter-class>
</filter>
<filter>
<filter-name>endpoints-api-controller</filter-name>
<filter-class>com.google.api.control.extensions.appengine.GoogleAppEngineControlFilter</filter-class>
<init-param>
<param-name>endpoints.projectId</param-name>
<param-value>${endpoints.project.id}</param-value>
</init-param>
<init-param>
<param-name>endpoints.serviceName</param-name>
<param-value>${endpoints.project.id}</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>endpoints-api-configuration</filter-name>
<servlet-name>EndpointsServlet</servlet-name>
</filter-mapping>
<filter-mapping>
<filter-name>endpoints-api-controller</filter-name>
<servlet-name>EndpointsServlet</servlet-name>
</filter-mapping>
deployendpoint.yaml
swagger: '2.0'
info:
title: MyEndPointTestApi771 API
description: Sample API on Cloud Endpoints with a Cloud Run, Cloud Function and App Engine with IAP backend
version: 1.0.0
host: MYCLOUDRUNSERVICENAME-HASHCODE-uc.a.run.app
basePath: /_ah/api
schemes:
- https
consumes:
- application/json
produces:
- application/json
x-google-backend:
address: https://MYPROJECTID.uc.r.appspot.com
jwt_audience: IAP_CREATED_WEBCLIENTID_FROM_CREDENTIALS
x-google-allow: all
paths:
/echo/v1/echoonly:
post:
summary: echoonly
operationId: EchoEchoonly
responses:
'200':
description: A successful response
schema:
$ref: "#/definitions/Entity"
security:
- google_id_token: []
/echo/v1/echoname/{name}:
post:
summary: echoname
operationId: EchoEchoname
parameters:
- in: path
name: name
required: true
type: string
responses:
'200':
description: A successful response
schema:
$ref: "#/definitions/Entity"
security:
- google_id_token: []
/echo/v1/echoage/{name}/{age}:
post:
summary: echoage
operationId: EchoEchoage
parameters:
- in: path
name: name
required: true
type: string
- in: path
name: age
required: true
type: string
responses:
'200':
description: A successful response
schema:
$ref: "#/definitions/Entity"
security:
- google_id_token: []
definitions:
_any:
type: "object"
Entity:
type: "object"
properties:
appId:
type: "string"
key:
$ref: "#/definitions/Key"
kind:
type: "string"
namespace:
type: "string"
parent:
$ref: "#/definitions/Key"
properties:
$ref: "#/definitions/Map_String_Object"
propertiesFrom:
$ref: "#/definitions/Entity"
Map_String_Object:
type: "object"
additionalProperties:
$ref: "#/definitions/_any"
JsonMap:
type: "object"
Key:
type: "object"
properties:
appId:
type: "string"
complete:
type: "boolean"
id:
type: "integer"
format: "int64"
kind:
type: "string"
name:
type: "string"
namespace:
type: "string"
parent:
$ref: "#/definitions/Key"
securityDefinitions:
api_key:
type: "apiKey"
name: "key"
in: "query"
google_id_token:
authorizationUrl: 'https://accounts.google.com/o/oauth2/v2/auth?client_id=IAP_CREATED_WEBCLIENTID_FROM_CREDENTIALS&response_type=code&scope=openid%20email&access_type=offline&redirect_uri=urn:ietf:wg:oauth:2.0:oob'
flow: "implicit"
type: "oauth2"
x-google-issuer: "https://accounts.google.com"
x-google-jwks_uri: "https://www.googleapis.com/oauth2/v3/certs"
x-google-audiences: "IAP_CREATED_WEBCLIENTID_FROM_CREDENTIALS"
Update
I could not figure this out and went with Spring #RestController which worked perfect, without having to worry about creating Endpoints API on Google Cloud Platform

Spring microservices SSO with ReactJS

I'm working on single-sign-on implementation in my project with microservice architecture application, using spring-cloud-netflix. At the moment I have done with OAuth2 service and gateway service.
Here's my security config for gateway:
/**
* SSO security config.
*/
#Configuration
#EnableZuulProxy
#EnableOAuth2Sso
#EnableWebSecurity
public class SsoSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private OAuth2ClientAuthenticationProcessingFilter ssoFilter;
#Autowired
private AuthenticationEntryPoint authenticationEntryPoint;
#Autowired
private SsoLogoutSuccessHandler logoutSuccessHandler;
/**
* SSO http config.
*
* #param http configurer
* #throws Exception exception
*/
#Override
public void configure(HttpSecurity http) throws Exception {
http
// .cors().and()
.csrf().disable()
.authorizeRequests()
.antMatchers("/", "/test", "/favicon.ico", "/sockjs-node/**", "/static/**", "/*.js", "/*.jpg",
"/rest/**", "/uaa/**", "/backend/**",
"/users/**", "/files/**", "/roles/**").permitAll()
.anyRequest().authenticated().and()
// .exceptionHandling().authenticationEntryPoint(authenticationEntryPoint).and()
.logout().logoutSuccessHandler(logoutSuccessHandler)
.and()
.sessionManagement().maximumSessions(1)
.expiredUrl("/")
.maxSessionsPreventsLogin(false);
// http.addFilterAfter(ssoFilter, BasicAuthenticationFilter.class);
}
/**
* OAuth2 config.
*/
#Configuration
protected static class OAuth2Config {
#Bean
public OAuth2ClientAuthenticationProcessingFilter ssoFilter(
SsoLoginSuccessHandler ssoLoginSuccessHandler,
OAuth2ClientContext beaconOAuth2ClientContext,
RemoteTokenServices remoteTokenServices,
OAuth2ProtectedResourceDetails resourceDetails) {
OAuth2ClientAuthenticationProcessingFilter filter =
new OAuth2ClientAuthenticationProcessingFilter("/login");
filter.setRestTemplate(new OAuth2RestTemplate(resourceDetails,
beaconOAuth2ClientContext));
filter.setTokenServices(remoteTokenServices);
// filter.setAuthenticationSuccessHandler(new SavedRequestAwareAuthenticationSuccessHandler());
return filter;
}
//
// #Bean
// public CorsConfigurationSource corsConfigurationSource() {
// final CorsConfiguration configuration = new CorsConfiguration();
// configuration.setAllowedOrigins(ImmutableList.of("*"));
// configuration.setAllowedMethods(ImmutableList.of("HEAD",
// "GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"));
// configuration.setAllowCredentials(true);
// configuration.setAllowedHeaders(
// ImmutableList.of("Authorization", "Cache-Control", "Content-Type"));
// final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
// source.registerCorsConfiguration("/**", configuration);
// return source;
// }
}
}
gateway yaml config
security:
oauth2:
client:
preEstablishedRedirectUri: http://localhost:3000/login
registeredRedirectUri: http://localhost:3000/login
accessTokenUri: http://localhost:10035/uaa/oauth/token
userAuthorizationUri: http://localhost:10035/uaa/oauth/authorize
clientId: react_app
clientSecret: react_app_secret
useCurrentUri: true
resource:
tokenInfoUri: http://localhost:10035/uaa/oauth/check_token
I'm bit confused, how i should authorize react app client, if it running on different port (webpack dev server, port 3000)?
One alternative is to setup a proxy using webpack, for example, in your webpack configuration add it like this:
devServer: {
contentBase: '/static',
historyApiFallback: true,
port: 3000,
compress: false,
inline: false,
hot: true,
host: '0.0.0.0',
proxy: {
'/api': { // <---- Intercept all calls to `/api`
target: 'http://localhost:8080', // <--- Your API server in a different port
changeOrigin: true,
secure: false,
},
},
},
The previous configuration setup a proxy, so whenever there's a request to /api, it will proxy the request to your server running in the other port.

fosuser+sonatauser ignore french translations

I am trying to make a custom labels for a customized register form and login form. I use sonatauserbundle and fosuserbundle, my userbundle hetis from SonataUserbundle.
<?php
namespace SCVBook\UserBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
class SCVBookUserBundle extends Bundle
{
/**
* {#inheritdoc}
*/
public function getParent()
{
return 'SonataUserBundle';
}
}
here is my config.file
framework:
#esi: ~
translator: { fallback: fr }
I copied FOSUserBundle.fr.yml to ACME/UserBundle/Resources/translations, but login and register still use the english translation.
here is a line in my login.html.twig
<label for="username"> {{ 'security.login.username'|trans({}, 'FOSUserBundle') }}</label>
it makes changes only when I change FOSUserBundle.en.yml in vendor/friendsofsymfony/.../Ressources/translations/FOSUserBundle.en.yml
ok found, just I forgot in app/parameters.yml to set locale to locale:fr, for config.yml
just let it as default.
framework:
#esi: ~
translator: { fallback: "%locale%" }

Resources