Tuesday, February 7, 2023

SQL Injection in Hibernate | Spring JDBC and Prevent it

Introduction:


SQL injection is a type of security vulnerability that allows an attacker to execute malicious SQL code on a database management system through a web application. This can allow an attacker to gain unauthorized access to sensitive information, modify or delete data, or perform other malicious actions on the database.

SQL injection occurs when user-supplied data is not properly sanitized or validated before being used in a database query. If the user-supplied data is directly included in the query, an attacker can manipulate the data to inject malicious SQL code into the query. The attacker's code is then executed by the database management system, allowing the attacker to carry out malicious actions on the database.







For this post, I share 3 techniques: how to do SQL injection and after how to easily prevent it.
for more common SQL Injection can you check SQL Injection Cheat Sheet.

NOTE: In this example the code using  Hibernate and JDBC Template with MySQL. let go.

Downloading and installing MySQL


docker pull mysql/mysql-server:latest

docker run -d -p  3306:3306  --name mysql-docker-container -e MYSQL_ROOT_PASSWORD=mypass -e MYSQL_DATABASE=test_db -e MYSQL_USER=test -e MYSQL_PASSWORD=test_pass mysql/mysql-server:latest


Script


CREATE TABLE customers ( id BIGINT PRIMARY KEY, name VARCHAR(128), password VARCHAR(128), lastname VARCHAR(128) );

INSERT INTO customers (id, name, password, lastname) VALUES (1, 'Henry','$2a$10$iWlXVjsSU9.X0oxEjUwyYe3EOc5X2hqacWY7uuyV0BwonTt5SapSu', 'test1'), (2, 'Henry','$2a$10$iWlXVjsSU9.X0oxEjUwyYe3EOc5X2hqacWY7uuyV0BwonTt5SapSu', 'test2'), (3, 'Henry','$2a$10$iWlXVjsSU9.X0oxEjUwyYe3EOc5X2hqacWY7uuyV0BwonTt5SapSu', 'test3'), (4, 'admin','81dc9bdb52d04dc20036dbd8313ed055', 'test4'), (5, 'User5','$2a$10$iWlXVjsSU9.X0oxEjUwyYe3EOc5X2hqacWY7uuyV0BwonTt5SapSu', 'test5'), (6, 'User6','$2a$10$iWlXVjsSU9.X0oxEjUwyYe3EOc5X2hqacWY7uuyV0BwonTt5SapSu', 'test6'), (7, 'User7','$2a$10$iWlXVjsSU9.X0oxEjUwyYe3EOc5X2hqacWY7uuyV0BwonTt5SapSu', 'test7'), (8, 'User8','$2a$10$iWlXVjsSU9.X0oxEjUwyYe3EOc5X2hqacWY7uuyV0BwonTt5SapSu', 'test8'), (9, 'User9', '$2a$10$iWlXVjsSU9.X0oxEjUwyYe3EOc5X2hqacWY7uuyV0BwonTt5SapSu','test9'), (10, 'User10','$2a$10$iWlXVjsSU9.X0oxEjUwyYe3EOc5X2hqacWY7uuyV0BwonTt5SapSu', 'test10');



Technology


  • Spring Boot 3.0.2
  • Java 17
  • Spring JDBC 6
  • Hibernate 6
  • Jakarta 3.1
  • Docker
  • Maven 
  • IntelliJ IDEA



SQL Injection Attack Samples


Line Comments Sample SQL Injection Attacks

  • Spring's JdbcTemplate:  Simple query to find By Name And Id, with concat and string replace.
 String sql = "SELECT  * from customers where name ='"+name+"' AND id="+id;
 var list = jdbcTemplate.query(sql,   (rs, rowNum) ->
             Customer.builder()
             .id(rs.getInt("id"))
            .name(rs.getString("name"))
            .build()
);

If we send these parameters:
var  name = "Henry' -- '";
var id = 1;
Should  return one record, however return all lists with name are (Henry). because the original query was changed this way  [SELECT  * from customers where name ='Henry' -- '' AND id=1].

If we use replace, the same thing happens.

String sql = "SELECT  * from customers where name =:name AND id=:id";
        sql = sql.replace(":name", name).replace(":id",String.valueOf(id));
        var list = jdbcTemplate.query(sql,   (rs, rowNum) ->
                Customer.builder()
                        .id(rs.getInt("id"))
                        .name(rs.getString("name"))
                        .build()
        );



If we send these parameters:
var  name = "'Henry' -- ";
var id = 1;

Should return one record, however return all lists with name are (Henry). because the original query was changed this way  [SELECT  * from customers where name ='Henry' --  AND id=1].

[
  1. {
    • "id":1,
    • "name":"Henry"
    },
  2. {
    • "id":2,
    • "name":"Henry"
    },
  3. {
    • "id":3,
    • "name":"Henry"
    }
  4. ...........


  • Hibernate 6:  Simple query to find By Name And Id, with concat and string replace.
String sql = "SELECT e FROM customers e where name='" + name + "'AND id="+id;
var list = entityManager
           .createQuery(sql, Customer.class)
          .getResultList();


If we send these parameters:
var  name = "'Henry' -- ";
var id = 1;

Awesome, Hibernate convert original query this way. 
Hibernate: select c1_0.id,c1_0.name from customers c1_0 where c1_0.name=('Henry'--'') and c1_0.id=1
And return one record. 
[
  1. {
    • "id":1,
    • "name":"Henry",
    • "password":"124",
    • "lastname":"test1"
    }
]

If we use replace:
String sql = "SELECT  e from customers e where name =:name AND lastname =:lastname";
        sql = sql.replace(":name", name).replace(":lastname",lastname);
        System.out.println(sql);
        var list = entityManager
                .createQuery(sql, Customer.class)
                .getResultList();

If we send these parameters:
var name = "'Henry' -- ";
var lastname = "'test1'";
Hibernate convert original query this way: [SELECT  e from customers e where name ='Henry' --  AND lastname ='test1']
However Hibernate not permit the query, it will be send next error:  org.hibernate.query.sqm.ParsingException

Bypassing Login Screens (SMO+)
SQL Injection 101, Login tricks
  • Spring's JdbcTemplate: Simple query to find By Name And password.
String sql = "SELECT  * from customers where name ='"+name+"' AND password='"+password+"'";
        var list = jdbcTemplate.query(sql,   (rs, rowNum) ->
                Customer.builder()
                        .id(rs.getInt("id"))
                        .name(rs.getString("name"))
                        .build()
        );

        return list;



If we send these parameters: (If we use .trim() to remove whitespace, some values in SQL injection don't work. however the good practice is the data should be properly sanitized).

/** No works
Henry' /*
Henry' or 1=1 /*
* */

// SQL injection Ok
//var name = "Henry' -- ";
//var name = "Henry' # ";
//var name = "Henry' or 1=1 -- ";
//var name = "Henry' or 1=1 # ";
var name = "Henry' or '1'='1 --";
var pass = "123";
Should return one record, however return all lists with name are (Henry)

[
  1. {
    • "id":1,
    • "name":"Henry"
    },
  2. {
    • "id":2,
    • "name":"Henry"
    },
  3. {
    • "id":3,
    • "name":"Henry"
    }
]
  • Hibernate 6:  Simple query to find By Name And password.
var  list = entityManager
                .createQuery("SELECT e FROM customers e where name='" + name + "' AND password='"+pass+"'", Customer.class)
                .getResultList();


If we send these parameters:

/** No works
Henry' --
Henry' #
Henry'/*
Henry' or 1=1 --
Henry' or 1=1 #
Henry' or 1=1 /*
* */

var name = "Henry' or '1'='1 --";
var pass = "123";
Should return one record, however return all lists with name are (Henry)
[
  1. {
    • "id":1,
    • "name":"Henry"
    },
  2. {
    • "id":2,
    • "name":"Henry"
    },
  3. {
    • "id":3,
    • "name":"Henry"
    }
]

Boolean SQL Injection Attacks

  • Spring's JdbcTemplate:  Simple query to find By Name.
  String sql = "SELECT  * from customers where name ='"+name+"'";
        var list = jdbcTemplate.query(sql,   (rs, rowNum) ->
                Customer.builder()
                        .id(rs.getInt("id"))
                        .name(rs.getString("name"))
                        .build()
        );

        return list;

If we send this parameter:

var name = "abc' or '1'='1";
Should not return any  record, however return all lists.
[
  1. {
    • "id":1,
    • "name":"Henry"
    },
  2. {
    • "id":2,
    • "name":"Henry"
    },
  3. {
    • "id":3,
    • "name":"Henry"
    }
  4. ...........
 
  • Hibernate:  Simple query to find By Name.
   var  list = entityManager
                .createQuery("SELECT e FROM customers e where name='" + name + "'", Customer.class)
                .getResultList();

If we send this parameter:

var name = "abc' or '1'='1";
Should not return any  record, however return all lists.
[
  1. {
    • "id":1,
    • "name":"Henry"
    },
  2. {
    • "id":2,
    • "name":"Henry"
    },
  3. {
    • "id":3,
    • "name":"Henry"
    }
  4. ...........


Prevent SQL Injection

  • Parameterized queries, also known as prepared statements, use placeholders to represent values in a query. The actual values are then passed as parameters to the query, separately from the SQL code. This makes it much more difficult for an attacker to inject malicious code into a query.

For e.g.

Spring's JdbcTemplate

 String sql = "SELECT  * from customers where name =? AND id=?";
        var list = jdbcTemplate.query(sql,   (rs, rowNum) ->
                Customer.builder()
                        .id(rs.getInt("id"))
                        .name(rs.getString("name"))
                        .build()
        ,new Object[]{name, id});

        return list;


  String sql = "SELECT  * from customers where name =:name AND id=:id";

        MapSqlParameterSource mapSqlParameterSource = new MapSqlParameterSource();
        mapSqlParameterSource.addValue("name", name);
        mapSqlParameterSource.addValue("id", id);


        var list = namedParameterJdbcTemplate.query(sql, mapSqlParameterSource,  (rs, rowNum) ->
                        Customer.builder()
                                .id(rs.getInt("id"))
                                .name(rs.getString("name"))
                                .build()
                );

        return list;


Hibernate 6


 var list = entityManager
                .createQuery("SELECT e FROM customers e where name=:name AND password=:password", Customer.class)
                  .setParameter("name", name)
                  .setParameter("password", pass)
                .getResultList();



  • keep your software up to date to ensure that any security vulnerabilities are patched.
  • Consider using a web application firewall (WAF). A WAF or web application firewall helps protect web applications by filtering and monitoring HTTP traffic between a web application and the Internet. It typically protects web applications from attacks such as cross-site forgery, cross-site-scripting (XSS), file inclusion, and SQL injection, among others.
  • Use of Properly Constructed Stored Procedures
  • Allow-list Input Validation


Test


Spring JDBC main classes:
  1. BooleanAttacksJDBCTemplate
  2. BypassingLoginAttacksJDBCTemplate
  3. LineCommentsAttacksJDBCTemplate
Hibernate 6 main classes:
  • BooleanAttacksHibernate
  • BypassingLoginAttacksHibernate
  • LineCommentsAttacksHibernate

Source Code


Here on GitHub.




References.

https://www.invicti.com/blog/web-security/sql-injection-cheat-sheet/
https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html
https://www.cloudflare.com/learning/ddos/glossary/web-application-firewall-waf/

































Wednesday, January 25, 2023

Spring Authorization Server 1.0 with LDAP - Spring Security 6

 

Introduction:


Spring Authorization Server is a framework that provides implementations of the OAuth 2.1 and OpenID Connect 1.0 specifications and other related specifications. It is built on top of Spring Security to provide a secure, light-weight, and customizable foundation for building OpenID Connect 1.0 Identity Providers and OAuth2 Authorization Server products.

Spring Authorization Server requires a Java 17 or higher Runtime Environment. 


Demo


In this demo, I integrated Spring Authorization Server 1.0 with LDAP.  In LDAP servers can use LDIF (LDAP Data Interchange Format) files to exchange user data. so I used test-server.ldif provide by spring example.



Technology


  • Spring Boot 3.0.1
  • Java 17
  • Spring Authorization Server 1.0
  • Maven 
  • IntelliJ IDEA


Configuration Spring Boot project 


Add Spring Authorization Server and  Spring Security as a dependencies, as in the following example:

   <dependencies>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-authorization-server</artifactId>
<version>1.0.0</version>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.ldap</groupId>
<artifactId>spring-ldap-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-ldap</artifactId>
</dependency>
<dependency>
<groupId>com.unboundid</groupId>
<artifactId>unboundid-ldapsdk</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>

</dependencies>



application.yml



The spring.ldap.embedded.ldif property inside application.yml lets Spring Boot pull in an LDIF data file. 
server:
port: 9000

spring:
ldap:
embedded:
base-dn: dc=springframework,dc=org
ldif: classpath:test-server.ldif
port: 8389
url: ldap://localhost:8389/

logging:
level:
root: INFO
org.springframework.web: INFO
org.springframework.security: INFO
org.springframework.security.oauth2: INFO
# org.springframework.boot.autoconfigure: DEBUG

Create a Simples Webs Controllers

@RestController
public class HelloResource {

@GetMapping("/hello")
public String hello(Principal principal) {
return "Hello "+principal.getName();
}
}

@Controller
public class AuthorizationController {

@GetMapping(value={"", "/", "landing"})
public String consent(Principal principal, Model model){

System.out.println("user "+principal.getName());
model.addAttribute("principalName", principal.getName());
return "landing";
}
}


Set up Spring Security


The ldapAuthentication() method configures things so that the user name at the login form is plugged into {0} such that it searches uid={0},ou=people,dc=springframework,dc=org in the LDAP server. Also, the passwordCompare() method configures the encoder and the name of the password’s attribute.

@Configuration
public class DefaultSecurityConfig {

@Bean
@Order(2)
public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http)
throws Exception {
http
.authorizeHttpRequests((authorize) -> authorize
.anyRequest().authenticated()
)
// Form login handles the redirect to the login page from the
// authorization server filter chain
.formLogin(Customizer.withDefaults());

return http.build();
}

/* @Bean
public UserDetailsService userDetailsService() {
UserDetails userDetails = User.withUsername("henry")
.password(passwordEncoder().encode("123"))
.roles("USER")
.build();

return new InMemoryUserDetailsManager(userDetails);
}*/
@Autowired
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.ldapAuthentication()
.userDnPatterns("uid={0},ou=people")
.groupSearchBase("ou=groups")
.contextSource()
.url("ldap://localhost:8389/dc=springframework,dc=org")
.and()
.passwordCompare()
.passwordEncoder(new BCryptPasswordEncoder())
.passwordAttribute("userPassword");
}
}

Set up Authorization Server


  • To get started, You need the minimum required components defined as a @Bean in a Spring @Configuration
  • The first bean is used to define the OAuth2 Protocol Endpoint.
  • The RegisteredClientRepository is the central component where new clients can be registered and existing clients can be queried. It is used by other components when following a specific protocol flow, such as client authentication, authorization grant processing, token introspection, dynamic client registration, and others.
  • com.nimbusds.jose.jwk.source.JWKSource for signing access tokens.
  • java.security.KeyPair with keys generated on startup used to create the JWKSource.
  • JwtDecoder for decoding signed access tokens.
  • AuthorizationServerSettings contains the configuration settings for the OAuth2 authorization server. It specifies the URI for the protocol endpoints as well as the issuer identifier. The default URI for the protocol endpoints are as follows:

@Configuration
public class AuthorizationServerConfig {

@Bean
@Order(1)
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http)
throws Exception {
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
http.getConfigurer(OAuth2AuthorizationServerConfigurer.class)
.oidc(Customizer.withDefaults()); // Enable OpenID Connect 1.0
http
// Redirect to the login page when not authenticated from the
// authorization endpoint
.exceptionHandling((exceptions) -> exceptions
.authenticationEntryPoint(
new LoginUrlAuthenticationEntryPoint("/login"))
)
// Accept access tokens for User Info and/or Client Registration
.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);

return http.build();
}

@Bean
public RegisteredClientRepository registeredClientRepository() {
RegisteredClient registeredClient = RegisteredClient.withId(UUID.randomUUID().toString())
.clientId("messaging-client")
.clientSecret("{noop}secret")
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
.redirectUri("http://127.0.0.1:9000/login/oauth2/code/messaging-client-oidc")
.redirectUri("http://127.0.0.1:9000/authorized")
.scope(OidcScopes.OPENID)
.scope(OidcScopes.PROFILE)
.scope("message.read")
.scope("message.write")
.clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build())
.build();

return new InMemoryRegisteredClientRepository(registeredClient);
}

@Bean
public JWKSource<SecurityContext> jwkSource() {
KeyPair keyPair = generateRsaKey();
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
RSAKey rsaKey = new RSAKey.Builder(publicKey)
.privateKey(privateKey)
.keyID(UUID.randomUUID().toString())
.build();
JWKSet jwkSet = new JWKSet(rsaKey);
return new ImmutableJWKSet<>(jwkSet);
}

private static KeyPair generateRsaKey() {
KeyPair keyPair;
try {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);
keyPair = keyPairGenerator.generateKeyPair();
}
catch (Exception ex) {
throw new IllegalStateException(ex);
}
return keyPair;
}

@Bean
public JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {
return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);
}

@Bean
public AuthorizationServerSettings authorizationServerSettings() {
return AuthorizationServerSettings.builder().build();
}

@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}

 

Run & Test


Run Spring Boot application with command: mvn spring-boot:run. by console, IntelliJ etc.

http://localhost:9000/landing





User: henry
Password: 123

Result this:


















Source Code


Here on GitHub.




References.

https://spring.io/projects/spring-authorization-server
https://spring.io/guides/gs/authenticating-ldap/
https://docs.spring.io/spring-security/reference/servlet/authentication/passwords/ldap.html
https://spring.io/blog/2022/02/21/spring-security-without-the-websecurityconfigureradapter
https://spring.io/guides/gs/authenticating-ldap/

















Thursday, January 12, 2023

Angular - HTTP interceptor

Introduction:

In Angular, an interceptor is a middleware that can be used to modify or intercept HTTP requests or responses before they are handled by the application.

To implement the HttpInterceptor interface provided by the @angular/common/http module. This interface requires implementing a single method called intercept, which takes in an HttpRequest object and a HttpHandler, and returns an Observable of HttpEvent.

By official documentation

Methods


intercept()


Identifies and handles a given HTTP request.

intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>>

Parameters


req HttpRequest<any> The outgoing request object to handle.

next HttpHandler The next interceptor in the chain, or the backend if no interceptors remain in the chain.

Returns


Observable<HttpEvent<any>>: An observable of the event stream.


Usage





I used jsonplaceholder fake json. Get, Post, Put, Patch and Delete, it is common to use OAuth2 token for to access backend app from angular frontend, so it is need to add next header. 

const clonedRequest = req.clone({
      headers: req.headers.set('Authorization'`Bearer ${token}`),
    });

Before to send to backend request. 


Auth Service (fake token)


Real token we will get from Open Source Identity and Access Management, Basic Auth or something like that.

import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root',
})
export class AuthService {
  public get token(): string {
    return '123456';
  }
}


Interceptor Service


Method called intercept that takes in two arguments: req: HttpRequest<any> and next: HttpHandler. Within the intercept method,  adding headers. After modifying the request, call the next.handle(req) method to continue with the request.

import { Injectable } from '@angular/core';
import {
  HttpInterceptor,
  HttpRequest,
  HttpHandler,
  HttpEvent,
from '@angular/common/http';
import { Observable } from 'rxjs';
import { AuthService } from '../auth.service';

@Injectable()
export class MyInterceptor implements HttpInterceptor {
  constructor(private authAuthService) {}

  intercept(
    reqHttpRequest<any>,
    nextHttpHandler
  ): Observable<HttpEvent<any>> {
    const token = this.auth.token;
    // Clone the request and add the new header
    const clonedRequest = req.clone({
      headers: req.headers.set('Authorization'`Bearer ${token}`),
    });

    // Pass the cloned request instead of the original request to the next handle
    console.log(clonedRequest);
    const keys = clonedRequest.headers.keys();
    alert(
      'Before request new header is added: \n' +
        keys.map((keyany=> `${key}${clonedRequest.headers.get(key)}`)
    );

    return next.handle(clonedRequest);
  }
}




HTTP Request Service


import { Injectable } from '@angular/core';
import { HttpClientHttpResponseHttpHeaders } from '@angular/common/http';
import { Observable } from 'rxjs';

const headers = new HttpHeaders().set(
  'content-type',
  'application/json; charset=UTF-8'
);
const endpoint = 'https://jsonplaceholder.typicode.com/posts';

@Injectable()
export class MyServiceService {
  constructor(private httpHttpClient) {}

  postData(dataany): Observable<HttpResponse<any>> {
    return this.http.post<any>(`${endpoint}`data, { headers: headers });
  }

  getData(idnumber): Observable<HttpResponse<any>> {
    return this.http.get<any>(`${endpoint}/${id}`);
  }

  updateData(dataanyidnumber): Observable<HttpResponse<any>> {
    return this.http.put<any>(`${endpoint}/${id}`data, { headers: headers });
  }

  patchData(dataanyidnumber): Observable<HttpResponse<any>> {
    return this.http.patch<any>(`${endpoint}/${id}`data, {
      headers: headers,
    });
  }

  deleteData(idnumber): Observable<HttpResponse<any>> {
    return this.http.delete<any>(`${endpoint}/${id}`);
  }
}



Component


import { ComponentVERSION } from '@angular/core';
import { MyServiceService } from './services/myservice/my-service.service';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent {
  name = 'HTTP interceptor';
  constructor(private _serviceMyServiceService) {}

  responseboolean;
  datastring;

  onGet(valueboolean) {
    this.response = value;
    this._service.getData(1).subscribe((dataany=> {
      this.data = data;
    });
  }

  onSave(valueboolean) {
    this.response = value;
    let json = JSON.stringify({
      title: 'foo',
      body: 'bar',
      userId: 1,
    });

    this._service.postData(json).subscribe((dataany=> {
      this.data = data;
    });
  }

  onPut(valueboolean) {
    this.response = value;

    let json = JSON.stringify({
      id: 1,
      title: 'foo',
      body: 'bar',
      userId: 1,
    });

    this._service.updateData(json1).subscribe((dataany=> {
      this.data = data;
    });
  }

  onPatch(valueboolean) {
    this.response = value;
    let json = JSON.stringify({
      title: 'foo',
    });

    this._service.patchData(json1).subscribe((dataany=> {
      this.data = data;
    });
  }

  onDelete(valueboolean) {
    this.response = value;
    this._service.deleteData(1).subscribe((dataany=> {
      this.data = data;
    });
  }
}




Test






















Here integration example Spring Boot, Angular, Ldap and Keycloak.

Source Code

Here on stackblitz.


References

https://angular.io/
https://jsonplaceholder.typicode.com/






Monday, January 2, 2023

Java Generics - List

  • Only instances of that type can be inserted
            List<T> list = new ArrayList<T>();
  • list − object of List interface.
  • T − The generic type parameter passed during list declaration.

Example:

import java.util.ArrayList;
import java.util.List;

public class GenericList <T>{

List<T> wrappedList;

public GenericList() {
this.wrappedList = new ArrayList<>();
}
public GenericList(List<T> wrappedList) {
this.wrappedList = wrappedList;
}

//get method with custom one
public T myGet(int index){
return wrappedList.get(index);
}

//add method with custom one
public void myAdd(T o){
wrappedList.add(o);
}

//remove method with custom one
public T myRemove(int index){
return wrappedList.remove(index);
}

@Override
public String toString() {
return wrappedList.toString();
}

public static void main(String[] args) {

GenericList<CharSequence> g = new GenericList<>();
g.myAdd("hi");
g.myAdd("hello");
var o = g.myGet(0);
System.out.println("Element 0 = "+o);

System.out.println("Wrapped List "+g.myRemove(0));

}

}

🚀 Spring Boot 3.5 → 4.0.3 Migration Summary

Import Changes, API Adjustments & Compatibility Results Migration summary from Spring Boot 3.5 → 4.0, including breaking changes, depend...