Crystal.Sea 4 ani în urmă
părinte
comite
00edd24274
16 a modificat fișierele cu 966 adăugiri și 5 ștergeri
  1. 6 0
      maxkey-protocols/maxkey-protocol-cas/build.gradle
  2. 184 0
      maxkey-protocols/maxkey-protocol-cas/src/main/java/org/maxkey/authz/cas/endpoint/CasRestV1Endpoint.java
  3. 6 0
      maxkey-protocols/maxkey-protocol-cas/src/main/java/org/maxkey/authz/cas/endpoint/ticket/CasConstants.java
  4. 58 0
      maxkey-protocols/maxkey-protocol-cas/src/main/java/org/maxkey/authz/cas/endpoint/ticket/ExpirationPolicy.java
  5. 63 0
      maxkey-protocols/maxkey-protocol-cas/src/main/java/org/maxkey/authz/cas/endpoint/ticket/ServiceTicket.java
  6. 21 1
      maxkey-protocols/maxkey-protocol-cas/src/main/java/org/maxkey/authz/cas/endpoint/ticket/ServiceTicketImpl.java
  7. 115 0
      maxkey-protocols/maxkey-protocol-cas/src/main/java/org/maxkey/authz/cas/endpoint/ticket/TicketGrantingTicket.java
  8. 198 0
      maxkey-protocols/maxkey-protocol-cas/src/main/java/org/maxkey/authz/cas/endpoint/ticket/TicketGrantingTicketImpl.java
  9. 66 0
      maxkey-protocols/maxkey-protocol-cas/src/main/java/org/maxkey/authz/cas/endpoint/ticket/TicketState.java
  10. 41 0
      maxkey-protocols/maxkey-protocol-cas/src/main/java/org/maxkey/authz/cas/endpoint/ticket/proxy/ProxyGrantingTicket.java
  11. 18 0
      maxkey-protocols/maxkey-protocol-cas/src/main/java/org/maxkey/authz/cas/endpoint/ticket/proxy/ProxyTicket.java
  12. 3 3
      maxkey-protocols/maxkey-protocol-cas/src/main/java/org/maxkey/authz/cas/endpoint/ticket/service/RandomServiceTicketServices.java
  13. 1 1
      maxkey-protocols/maxkey-protocol-cas/src/main/java/org/maxkey/authz/cas/endpoint/ticket/service/RedisTicketServices.java
  14. 132 0
      maxkey-protocols/maxkey-protocol-cas/src/test/java/org/maxkey/web/authorize/endpoint/CasRestClient.java
  15. 51 0
      maxkey-protocols/maxkey-protocol-cas/src/test/java/org/maxkey/web/authorize/endpoint/RestTestClient.java
  16. 3 0
      maxkey-web-maxkey/src/main/java/org/maxkey/MaxKeyMvcConfig.java

+ 6 - 0
maxkey-protocols/maxkey-protocol-cas/build.gradle

@@ -6,6 +6,12 @@ dependencies {
 	//local jars
 	compile fileTree(dir: '../maxkey-lib/*/', include: '*.jar')
 	
+	
+	testCompile group: 'org.pac4j', name: 'pac4j-core', version: '3.1.0'
+	// https://mvnrepository.com/artifact/org.pac4j/pac4j-cas
+	testCompile group: 'org.pac4j', name: 'pac4j-cas', version: '3.1.0'
+	
+	
 	compile project(":maxkey-core")
 	compile project(":maxkey-persistence")
    	compile project(":maxkey-protocols:maxkey-protocol-authorize")

+ 184 - 0
maxkey-protocols/maxkey-protocol-cas/src/main/java/org/maxkey/authz/cas/endpoint/CasRestV1Endpoint.java

@@ -0,0 +1,184 @@
+/*
+ * Copyright [2020] [MaxKey of copyright http://www.maxkey.top]
+ * 
+ * Licensed 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.maxkey.authz.cas.endpoint;
+
+import java.util.List;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.maxkey.authn.BasicAuthentication;
+import org.maxkey.authn.realm.AbstractAuthenticationRealm;
+import org.maxkey.authz.cas.endpoint.ticket.CasConstants;
+import org.maxkey.authz.cas.endpoint.ticket.ServiceTicketImpl;
+import org.maxkey.authz.cas.endpoint.ticket.TicketGrantingTicketImpl;
+import org.maxkey.domain.UserInfo;
+import org.maxkey.domain.apps.AppsCasDetails;
+import org.maxkey.persistence.db.PasswordPolicyValidator;
+import org.maxkey.web.WebContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.authentication.BadCredentialsException;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+
+/**
+ * @author Crystal.Sea
+ * https://apereo.github.io/cas/6.2.x/protocol/REST-Protocol.html
+ */
+@Controller
+public class CasRestV1Endpoint  extends CasBaseAuthorizeEndpoint{
+	final static Logger _logger = LoggerFactory.getLogger(CasRestV1Endpoint.class);
+	
+	@Autowired
+    protected PasswordPolicyValidator passwordPolicyValidator;
+    
+    @Autowired
+    @Qualifier("authenticationRealm")
+    protected AbstractAuthenticationRealm authenticationRealm;
+    
+
+	@RequestMapping(value="/authz/cas/v1/tickets", 
+	        method=RequestMethod.POST, 
+	        consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
+    public ResponseEntity<String> casLoginRestTickets(
+            HttpServletRequest request,
+            HttpServletResponse response,
+           
+            @RequestParam(value=CasConstants.PARAMETER.SERVICE,required=false) String casService,
+            @RequestParam(value=CasConstants.PARAMETER.REST_USERNAME,required=true) String username,
+            @RequestParam(value=CasConstants.PARAMETER.REST_PASSWORD,required=true) String password){
+	    try {
+    	    if (password == null || password.isEmpty()) {
+                throw new BadCredentialsException("No credentials are provided or extracted to authenticate the REST request");
+            }
+    	    
+            AbstractAuthenticationRealm authenticationRealm = 
+                    (AbstractAuthenticationRealm) WebContext.getBean("authenticationRealm");
+            UserInfo loadeduserInfo = authenticationRealm.loadUserInfo(username, "");
+            if (loadeduserInfo != null) {
+                
+                authenticationRealm.passwordMatches(loadeduserInfo, password);
+                
+                passwordPolicyValidator.passwordPolicyValid(loadeduserInfo);
+                
+                WebContext.setUserInfo(loadeduserInfo);
+                BasicAuthentication authentication =new BasicAuthentication();
+                authentication.setUsername(username);
+                authentication.setPassword(password);
+                authentication.setAuthType("basic");
+                
+                UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
+                        new UsernamePasswordAuthenticationToken(
+                                authentication, 
+                                "PASSWORD", 
+                                authenticationRealm.grantAuthority(loadeduserInfo)
+                        );
+
+                authentication.setAuthenticated(true);
+                WebContext.setAuthentication(usernamePasswordAuthenticationToken);
+                WebContext.setUserInfo(loadeduserInfo);
+
+                authenticationRealm.insertLoginHistory(loadeduserInfo, "CAS", "", "", "SUCCESS");
+                
+                TicketGrantingTicketImpl ticketGrantingTicket=new TicketGrantingTicketImpl("Random",WebContext.getAuthentication(),null);
+                
+                String ticket=ticketServices.createTicket(ticketGrantingTicket);
+                String location = applicationConfig.getServerPrefix()+"/authz/cas/v1/tickets/" + ticket;
+                HttpHeaders headers = new HttpHeaders();
+                headers.add("location", location);
+                return new ResponseEntity<>("Location: " + location, headers ,HttpStatus.CREATED);
+                
+            }else {
+    	        String message = WebContext.getI18nValue("login.error.username");
+                _logger.debug("login user  " + username + " not in this System ." + message);
+                throw new BadCredentialsException(WebContext.getI18nValue("login.error.username"));
+    	    }
+	    } catch (final AuthenticationException e) {
+	        _logger.error("BadCredentialsException ", e);
+            return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST);
+        } catch (final Exception e) {
+            
+            _logger.error("Exception ", e);
+            return new ResponseEntity<>(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
+        }
+	}
+	
+	   @RequestMapping(value="/authz/cas/v1/tickets/{ticketGrantingTicket}", 
+	            method=RequestMethod.POST, 
+	            consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
+	    public ResponseEntity<String> requestServiceTicket(
+	            HttpServletRequest request,
+	            HttpServletResponse response,
+	            @PathVariable("ticketGrantingTicket") String ticketGrantingTicket,
+	            @RequestParam(value=CasConstants.PARAMETER.SERVICE,required=false) String casService,
+	            @RequestParam(value=CasConstants.PARAMETER.RENEW,required=false) String renew,
+	            @RequestParam(value=CasConstants.PARAMETER.REST_USERNAME,required=false) String username,
+	            @RequestParam(value=CasConstants.PARAMETER.REST_PASSWORD,required=false) String password){
+	       try {
+            TicketGrantingTicketImpl ticketGrantingTicketImpl = 
+                    (TicketGrantingTicketImpl) ticketServices.consumeTicket(ticketGrantingTicket);
+            AppsCasDetails casDetails=new AppsCasDetails();
+            if(casService.startsWith("http")) {
+                casDetails.setService(casService);
+                
+                List<AppsCasDetails> casDetailsList=casDetailsService.query(casDetails);
+                
+                casDetails=(casDetailsList!=null && casDetailsList.size()==1)?casDetailsList.get(0):null;
+            }else {
+                casDetails=casDetailsService.getAppDetails(casService);
+            }
+            
+            ServiceTicketImpl serviceTicket=new ServiceTicketImpl(ticketGrantingTicketImpl.getAuthentication(),casDetails);
+            String ticket=ticketServices.createTicket(serviceTicket);
+            return new ResponseEntity<>(ticket, HttpStatus.OK);
+            
+        } catch (Exception e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        }
+	       return new ResponseEntity<>("", HttpStatus.BAD_REQUEST);
+	   }
+	@RequestMapping(value="/authz/cas/v1/users", 
+            method=RequestMethod.POST, 
+            consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
+    public ResponseEntity<String> casLoginRestUsers(
+            HttpServletRequest request,
+            HttpServletResponse response,
+            @RequestParam(value=CasConstants.PARAMETER.SERVICE,required=false) String casService,
+            @RequestParam(value=CasConstants.PARAMETER.REST_USERNAME,required=true) String username,
+            @RequestParam(value=CasConstants.PARAMETER.REST_PASSWORD,required=true) String password){
+        
+       return null;
+    }
+	
+}

+ 6 - 0
maxkey-protocols/maxkey-protocol-cas/src/main/java/org/maxkey/authz/cas/endpoint/ticket/CasConstants.java

@@ -57,6 +57,10 @@ public class CasConstants {
 
 	    /** Constant representing the pgtIou parameter in the request. */
 		public static final String PROXY_GRANTING_TICKET_IOU = "pgtIou";
+		
+		public static final String REST_USERNAME = "username";
+		
+		public static final String REST_PASSWORD = "password";
 	}
 	
 	public  static final class  FORMAT_TYPE {
@@ -83,6 +87,8 @@ public class CasConstants {
 		public static final String PROXY_GRANTING_TICKET_PREFIX = "PGT";
 	    /** The prefix to use when generating an id for a Proxy Granting Ticket IOU. */
 		public static final String PROXY_GRANTING_TICKET_IOU_PREFIX = "PGTIOU";
+		
+		public static final String TICKET_GRANTING_TICKET_PREFIX = "TGT";
 	}
 	
 	/* CAS Protocol Error Codes. **/

+ 58 - 0
maxkey-protocols/maxkey-protocol-cas/src/main/java/org/maxkey/authz/cas/endpoint/ticket/ExpirationPolicy.java

@@ -0,0 +1,58 @@
+package org.maxkey.authz.cas.endpoint.ticket;
+
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+
+import java.io.Serializable;
+
+/**
+ * Strategy that determines if the ticket is expired. Implementations of the
+ * Expiration Policy define their own rules on what they consider an expired
+ * Ticket to be.
+ *
+ * @author Scott Battaglia
+ * @see Ticket
+ * @since 3.0.0
+ */
+@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
+public interface ExpirationPolicy extends Serializable {
+
+    /**
+     * Method to determine if a Ticket has expired or not, based on the policy.
+     *
+     * @param ticketState The snapshot of the current ticket state
+     * @return true if the ticket is expired, false otherwise.
+     */
+    boolean isExpired(TicketState ticketState);
+
+    /**
+     * Method to determine the actual TTL of a ticket, based on the policy.
+     *
+     * @param ticketState The snapshot of the current ticket state
+     * @return The time to live in seconds. A zero value indicates the time duration is not supported or is inactive.
+     */
+    default Long getTimeToLive(final TicketState ticketState) {
+        return getTimeToLive();
+    }
+
+    /**
+     * Describes the time duration where this policy should consider the item alive.
+     * Once this time passes, the item is considered expired and dead.
+     *
+     * @return time to live in seconds. A zero value indicates the time duration is not supported or is inactive.
+     */
+    Long getTimeToLive();
+
+    /**
+     * Describes the idle time duration for the item.
+     *
+     * @return idle time in seconds. A zero value indicates the time duration is not supported or is inactive. Unit of measure is defined by the implementation.
+     */
+    Long getTimeToIdle();
+
+    /**
+     * Gets name of this expiration policy.
+     *
+     * @return the name
+     */
+    String getName();
+}

+ 63 - 0
maxkey-protocols/maxkey-protocol-cas/src/main/java/org/maxkey/authz/cas/endpoint/ticket/ServiceTicket.java

@@ -0,0 +1,63 @@
+package org.maxkey.authz.cas.endpoint.ticket;
+
+import org.maxkey.authz.cas.endpoint.ticket.proxy.ProxyGrantingTicket;
+import org.springframework.security.core.Authentication;
+
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+
+/**
+ * Interface for a Service Ticket. A service ticket is used to grant access to a
+ * specific service for a principal. A Service Ticket is generally a one-time
+ * use ticket.
+ *
+ * @author Scott Battaglia
+ * @since 3.0.0
+ */
+@JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include= JsonTypeInfo.As.PROPERTY)
+public interface ServiceTicket extends Ticket {
+
+    /**
+     * Prefix generally applied to unique ids generated
+     * by UniqueTicketIdGenerator.
+     */
+    String PREFIX = "ST";
+
+    /**
+     * Retrieve the service this ticket was given for.
+     *
+     * @return the server.
+     */
+    Service getService();
+
+    /**
+     * Determine if this ticket was created at the same time as a
+     * TicketGrantingTicket.
+     *
+     * @return true if it is, false otherwise.
+     */
+    boolean isFromNewLogin();
+
+    /**
+     * Attempts to ensure that the service specified matches the service associated with the ticket.
+     *
+     * @param service The incoming service to match this service ticket against.
+     * @return true, if the match is successful.
+     */
+    boolean isValidFor(Service service);
+
+    /**
+     * Method to grant a TicketGrantingTicket from this service to the
+     * authentication. Analogous to the ProxyGrantingTicket.
+     *
+     * @param id               The unique identifier for this ticket.
+     * @param authentication   The Authentication we wish to grant a ticket for.
+     * @param expirationPolicy expiration policy associated with this ticket
+     * @return The ticket granting ticket.
+     * @throws AbstractTicketException ticket exception thrown when generating the ticket
+     * @since 4.2
+     */
+    ProxyGrantingTicket grantProxyGrantingTicket(String id,
+                                                 Authentication authentication,
+                                                 ExpirationPolicy expirationPolicy)
+                                                 throws Exception;
+}

+ 21 - 1
maxkey-protocols/maxkey-protocol-cas/src/main/java/org/maxkey/authz/cas/endpoint/ticket/ServiceTicketImpl.java

@@ -18,6 +18,7 @@
 package org.maxkey.authz.cas.endpoint.ticket;
 
 import org.apache.commons.lang3.builder.EqualsBuilder;
+import org.maxkey.authz.cas.endpoint.ticket.proxy.ProxyGrantingTicket;
 import org.maxkey.domain.apps.AppsCasDetails;
 import org.springframework.security.core.Authentication;
 
@@ -33,7 +34,7 @@ import javax.persistence.Column;
  * @since 3.0.0
  */
 
-public class ServiceTicketImpl extends AbstractTicket {
+public class ServiceTicketImpl extends AbstractTicket  implements ServiceTicket{
     
     private static final long serialVersionUID = -4223319704861765405L;
 
@@ -112,4 +113,23 @@ public class ServiceTicketImpl extends AbstractTicket {
                 .isEquals();
     }
 
+    @Override
+    public Service getService() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public boolean isFromNewLogin() {
+        // TODO Auto-generated method stub
+        return false;
+    }
+
+    @Override
+    public ProxyGrantingTicket grantProxyGrantingTicket(String id, Authentication authentication,
+            ExpirationPolicy expirationPolicy) throws Exception {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
 }

+ 115 - 0
maxkey-protocols/maxkey-protocol-cas/src/main/java/org/maxkey/authz/cas/endpoint/ticket/TicketGrantingTicket.java

@@ -0,0 +1,115 @@
+package org.maxkey.authz.cas.endpoint.ticket;
+
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+
+import org.maxkey.domain.apps.AppsCasDetails;
+import org.springframework.security.core.Authentication;
+
+/**
+ * Interface for a ticket granting ticket. A TicketGrantingTicket is the main
+ * access into the CAS service layer. Without a TicketGrantingTicket, a user of
+ * CAS cannot do anything.
+ *
+ * @author Scott Battaglia
+ * @since 3.0.0
+ */
+@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
+public interface TicketGrantingTicket extends Ticket {
+
+    /**
+     * The prefix to use when generating an id for a Ticket Granting Ticket.
+     */
+    String PREFIX = "TGT";
+
+    /**
+     * Method to retrieve the authentication.
+     *
+     * @return the authentication
+     */
+    Authentication getAuthentication();
+
+    /**
+     * Grant a ServiceTicket for a specific service.
+     *
+     * @param id                         The unique identifier for this ticket.
+     * @param service                    The service for which we are granting a ticket
+     * @param expirationPolicy           the expiration policy.
+     * @param credentialProvided         current credential event for issuing this ticket. Could be null.
+     * @param onlyTrackMostRecentSession track the most recent session by keeping the latest service ticket
+     * @return the service ticket granted to a specific service for the principal of the TicketGrantingTicket
+     */
+    ServiceTicket grantServiceTicket(String id, Service service,
+                                     AppsCasDetails casDetails,
+                                     ExpirationPolicy expirationPolicy,
+                                     boolean credentialProvided,
+                                     boolean onlyTrackMostRecentSession);
+
+    /**
+     * Gets an immutable map of service ticket and services accessed by this ticket-granting ticket.
+     *
+     * @return an immutable map of service ticket and services accessed by this ticket-granting ticket.
+     */
+    Map<String, Service> getServices();
+
+    /**
+     * Gets proxy granting tickets created by this TGT.
+     *
+     * @return the proxy granting tickets
+     */
+    Map<String, Service> getProxyGrantingTickets();
+
+    /**
+     * Remove all services of the TGT (at logout).
+     */
+    void removeAllServices();
+
+    /**
+     * Convenience method to determine if the TicketGrantingTicket is the root
+     * of the hierarchy of tickets.
+     *
+     * @return true if it has no parent, false otherwise.
+     */
+    boolean isRoot();
+
+    /**
+     * Gets the ticket-granting ticket at the root of the ticket hierarchy.
+     *
+     * @return Non -null root ticket-granting ticket.
+     */
+    TicketGrantingTicket getRoot();
+
+    /**
+     * Gets all authentications ({@link #getAuthentication()} from this
+     * instance and all dependent tickets that reference this one.
+     *
+     * @return Non -null list of authentication associated with this ticket in leaf-first order.
+     */
+    List<Authentication> getChainedAuthentications();
+
+
+    /**
+     * Gets the service that produced a proxy-granting ticket.
+     *
+     * @return Service that produced proxy-granting ticket or null if this is not a proxy-granting ticket.
+     * @since 4.1
+     */
+    Service getProxiedBy();
+
+    /**
+     * Gets descendant tickets. These are generally ticket ids
+     * whose life-line is separate from the TGT until and unless
+     * the TGT goes away entirely. Things such as OAuth access tokens
+     * are a good example of such linked tickets.
+     *
+     * @return the descendant tickets
+     * @since 5.1
+     */
+    default Collection<String> getDescendantTickets() {
+        return new HashSet<>(0);
+    }
+}

+ 198 - 0
maxkey-protocols/maxkey-protocol-cas/src/main/java/org/maxkey/authz/cas/endpoint/ticket/TicketGrantingTicketImpl.java

@@ -0,0 +1,198 @@
+package org.maxkey.authz.cas.endpoint.ticket;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+
+import org.apache.commons.lang3.StringUtils;
+import org.maxkey.domain.apps.AppsCasDetails;
+import org.springframework.lang.NonNull;
+import org.springframework.security.core.Authentication;
+
+import javax.persistence.Column;
+import javax.persistence.DiscriminatorColumn;
+import javax.persistence.DiscriminatorValue;
+import javax.persistence.Entity;
+import javax.persistence.Lob;
+import javax.persistence.ManyToOne;
+import javax.persistence.Table;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Concrete implementation of a TicketGrantingTicket. A TicketGrantingTicket is
+ * the global identifier of a principal into the system. It grants the Principal
+ * single-sign on access to any service that opts into single-sign on.
+ * Expiration of a TicketGrantingTicket is controlled by the ExpirationPolicy
+ * specified as object creation.
+ *
+ * @author Scott Battaglia
+ * @since 3.0.0
+ */
+@Entity
+@Table(name = "TICKETGRANTINGTICKET")
+@DiscriminatorColumn(name = "TYPE")
+@DiscriminatorValue(TicketGrantingTicket.PREFIX)
+@JsonIgnoreProperties(ignoreUnknown = true)
+@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
+
+public class TicketGrantingTicketImpl extends AbstractTicket implements TicketGrantingTicket {
+
+    /**
+     * Unique Id for serialization.
+     */
+    private static final long serialVersionUID = -8608149809180911599L;
+
+    /**
+     * The authenticated object for which this ticket was generated for.
+     */
+    @Lob
+    @Column(name = "AUTHENTICATION", nullable = false, length = Integer.MAX_VALUE)
+    private Authentication authentication;
+    
+    /**
+     * Service that produced a proxy-granting ticket.
+     */
+    @Lob
+    @Column(name = "PROXIED_BY", length = Integer.MAX_VALUE)
+    private Service proxiedBy;
+
+    /**
+     * The services associated to this ticket.
+     */
+    @Lob
+    @Column(name = "SERVICES_GRANTED_ACCESS_TO", nullable = false, length = Integer.MAX_VALUE)
+    private HashMap<String, Service> services = new HashMap<>();
+
+    /**
+     * The {@link TicketGrantingTicket} this is associated with.
+     */
+    @ManyToOne(targetEntity = TicketGrantingTicketImpl.class)
+    private TicketGrantingTicket ticketGrantingTicket;
+
+    /**
+     * The PGTs associated to this ticket.
+     */
+    @Lob
+    @Column(name = "PROXY_GRANTING_TICKETS", nullable = false, length = Integer.MAX_VALUE)
+    private HashMap<String, Service> proxyGrantingTickets = new HashMap<>();
+
+    /**
+     * The ticket ids which are tied to this ticket.
+     */
+    @Lob
+    @Column(name = "DESCENDANT_TICKETS", nullable = false, length = Integer.MAX_VALUE)
+    private HashSet<String> descendantTickets = new HashSet<>();
+
+    /**
+     * Constructs a new TicketGrantingTicket.
+     * May throw an {@link IllegalArgumentException} if the Authentication object is null.
+     *
+     * @param id                         the id of the Ticket
+     * @param proxiedBy                  Service that produced this proxy ticket.
+     * @param parentTicketGrantingTicket the parent ticket
+     * @param authentication             the Authentication request for this ticket
+     * @param policy                     the expiration policy for this ticket.
+     */
+    @JsonCreator
+    public TicketGrantingTicketImpl(@JsonProperty("id") final String id, @JsonProperty("proxiedBy") final Service proxiedBy,
+                                    @JsonProperty("ticketGrantingTicket") final TicketGrantingTicket parentTicketGrantingTicket,
+                                    @NonNull @JsonProperty("authentication") final Authentication authentication, @JsonProperty("expirationPolicy") final ExpirationPolicy policy) {
+        if (parentTicketGrantingTicket != null && proxiedBy == null) {
+            throw new IllegalArgumentException("Must specify proxiedBy when providing parent TGT");
+        }
+        this.ticketGrantingTicket = parentTicketGrantingTicket;
+        this.authentication = authentication;
+        this.proxiedBy = proxiedBy;
+    }
+
+    /**
+     * Constructs a new TicketGrantingTicket without a parent
+     * TicketGrantingTicket.
+     *
+     * @param id             the id of the Ticket
+     * @param authentication the Authentication request for this ticket
+     * @param policy         the expiration policy for this ticket.
+     */
+    public TicketGrantingTicketImpl(final String id, final Authentication authentication, final ExpirationPolicy policy) {
+        this(id, null, null, authentication, policy);
+    }
+    
+    @Override
+    public synchronized ServiceTicket grantServiceTicket(final String id, final Service service, AppsCasDetails casDetails,final ExpirationPolicy expirationPolicy,
+                                                         final boolean credentialProvided, final boolean onlyTrackMostRecentSession) {
+        final ServiceTicket serviceTicket = new ServiceTicketImpl(authentication,casDetails);
+        return serviceTicket;
+    }
+
+    /**
+     * Normalize the path of a service by removing the query string and everything after a semi-colon.
+     *
+     * @param service the service to normalize
+     * @return the normalized path
+     
+    private static String normalizePath(final Service service) {
+        String path = service.getId();
+        path = StringUtils.substringBefore(path, "?");
+        path = StringUtils.substringBefore(path, ";");
+        path = StringUtils.substringBefore(path, "#");
+        return path;
+    }
+*/
+    /**
+     * Remove all services of the TGT (at logout).
+     */
+    @Override
+    public void removeAllServices() {
+        this.services.clear();
+    }
+
+
+    public String getPrefix() {
+        return TicketGrantingTicket.PREFIX;
+    }
+
+    @Override
+    public Map<String, Service> getServices() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public Map<String, Service> getProxyGrantingTickets() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public Service getProxiedBy() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public boolean isRoot() {
+        // TODO Auto-generated method stub
+        return false;
+    }
+
+    @Override
+    public TicketGrantingTicket getRoot() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public List<Authentication> getChainedAuthentications() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+
+}

+ 66 - 0
maxkey-protocols/maxkey-protocol-cas/src/main/java/org/maxkey/authz/cas/endpoint/ticket/TicketState.java

@@ -0,0 +1,66 @@
+package org.maxkey.authz.cas.endpoint.ticket;
+
+import java.time.ZonedDateTime;
+
+import org.springframework.security.core.Authentication;
+
+/**
+ * @author Scott Battaglia
+ * @since 3.0.0
+ */
+public interface TicketState {
+
+    /**
+     * Returns the number of times a ticket was used.
+     *
+     * @return the number of times the ticket was used.
+     */
+    int getCountOfUses();
+
+    /**
+     * Returns the last time the ticket was used.
+     *
+     * @return the last time the ticket was used.
+     */
+    ZonedDateTime getLastTimeUsed();
+
+    /**
+     * Get the second to last time used.
+     *
+     * @return the previous time used.
+     */
+    ZonedDateTime getPreviousTimeUsed();
+
+    /**
+     * Get the time the ticket was created.
+     *
+     * @return the creation time of the ticket.
+     */
+    ZonedDateTime getCreationTime();
+
+    /**
+     * Authentication information from the ticket. This may be null.
+     *
+     * @return the authentication information.
+     */
+    Authentication getAuthentication();
+
+    /**
+     * Method to retrieve the TicketGrantingTicket that granted this ticket.
+     *
+     * @return the ticket or null if it has no parent
+     */
+    TicketGrantingTicket getTicketGrantingTicket();
+    
+    /**
+     * Records the <i>previous</i> last time this ticket was used as well as
+     * the last usage time. The ticket usage count is also incremented.
+     * <p>Tickets themselves are solely responsible to maintain their state. The
+     * determination of  ticket usage is left up to the implementation and
+     * the specific ticket type.
+     *
+     * @see ExpirationPolicy
+     * @since 5.0.0
+     */
+    void update();
+}

+ 41 - 0
maxkey-protocols/maxkey-protocol-cas/src/main/java/org/maxkey/authz/cas/endpoint/ticket/proxy/ProxyGrantingTicket.java

@@ -0,0 +1,41 @@
+package org.maxkey.authz.cas.endpoint.ticket.proxy;
+
+import org.maxkey.authz.cas.endpoint.ticket.ExpirationPolicy;
+import org.maxkey.authz.cas.endpoint.ticket.Service;
+import org.maxkey.authz.cas.endpoint.ticket.TicketGrantingTicket;
+
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+
+/**
+ * Interface for a proxy granting ticket. A proxy-granting ticket is an opaque string that is
+ * used by a service to obtain proxy tickets for obtaining access to a back-end service on behalf of a client.
+ * Proxy-granting tickets are obtained from CAS upon validation of a service ticket or a proxy ticket.
+ *
+ * @author Misagh Moayyed
+ * @since 4.2
+ */
+@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
+public interface ProxyGrantingTicket extends TicketGrantingTicket {
+
+    /** The prefix to use when generating an id for a Proxy Granting Ticket. */
+    String PROXY_GRANTING_TICKET_PREFIX = "PGT";
+
+    /** The prefix to use when generating an id for a Proxy Granting Ticket IOU. */
+    String PROXY_GRANTING_TICKET_IOU_PREFIX = "PGTIOU";
+
+    /**
+     * Grant a proxy ticket for a specific service.
+     *
+     * @param id The unique identifier for this ticket.
+     * @param service The service for which we are granting a ticket
+     * @param expirationPolicy the expiration policy.
+     * @param onlyTrackMostRecentSession track the most recent session by keeping the latest service ticket
+     * @return the service ticket granted to a specific service for the
+     * principal of the TicketGrantingTicket
+     */
+    ProxyTicket grantProxyTicket(String id, Service service,
+                                 ExpirationPolicy expirationPolicy,
+                                 boolean onlyTrackMostRecentSession);
+
+}
+

+ 18 - 0
maxkey-protocols/maxkey-protocol-cas/src/main/java/org/maxkey/authz/cas/endpoint/ticket/proxy/ProxyTicket.java

@@ -0,0 +1,18 @@
+package org.maxkey.authz.cas.endpoint.ticket.proxy;
+
+import org.maxkey.authz.cas.endpoint.ticket.ServiceTicket;
+
+/**
+ * The {@link ProxyTicket} represents a CAS proxy ticket. A proxy ticket is an opaque string that a
+ * service uses as a credential to obtain access to a back-end service on behalf of a client.
+ * Proxy tickets are obtained from CAS upon a service’s
+ * presentation of a valid {@link ProxyGrantingTicket}
+ * and a service identifier for the back-end service to which it is connecting.
+ *
+ * @author Misagh Moayyed
+ * @since 4.2
+ */
+public interface ProxyTicket extends ServiceTicket {
+    /** Proxy ticket prefix applied to unique ids. */
+    String PROXY_TICKET_PREFIX = "PT";
+}

+ 3 - 3
maxkey-protocols/maxkey-protocol-cas/src/main/java/org/maxkey/authz/cas/endpoint/ticket/service/RandomServiceTicketServices.java

@@ -46,9 +46,9 @@ public abstract class RandomServiceTicketServices implements TicketServices {
 			ticketId = generator.getNewTicketId(CasConstants.PREFIX.SERVICE_TICKET_PREFIX);
 		}else if(ticket.getClass().getSimpleName().equalsIgnoreCase("ProxyTicketImpl")){
 			ticketId = generator.getNewTicketId(CasConstants.PREFIX.PROXY_TICKET_PREFIX);
-		}else if(ticket.getClass().getSimpleName().equalsIgnoreCase("ProxyTicketImpl")){
-			ticketId = generator.getNewTicketId(CasConstants.PREFIX.PROXY_TICKET_PREFIX);
-		}else if(ticket.getClass().getSimpleName().equalsIgnoreCase("ProxyTicketImpl")){
+		}else if(ticket.getClass().getSimpleName().equalsIgnoreCase("TicketGrantingTicketImpl")){
+			ticketId = generator.getNewTicketId(CasConstants.PREFIX.TICKET_GRANTING_TICKET_PREFIX);
+		}else {
 			ticketId = generator.getNewTicketId(CasConstants.PREFIX.PROXY_TICKET_PREFIX);
 		}
 		store(ticketId, ticket);

+ 1 - 1
maxkey-protocols/maxkey-protocol-cas/src/main/java/org/maxkey/authz/cas/endpoint/ticket/service/RedisTicketServices.java

@@ -28,7 +28,7 @@ public class RedisTicketServices extends RandomServiceTicketServices {
 	
 	RedisConnectionFactory connectionFactory;
 	
-	public static String PREFIX="REDIS_CAS_SERVICE_TICKET_";
+	public static String PREFIX="REDIS_CAS_TICKET_";
 	/**
 	 * @param connectionFactory
 	 */

+ 132 - 0
maxkey-protocols/maxkey-protocol-cas/src/test/java/org/maxkey/web/authorize/endpoint/CasRestClient.java

@@ -0,0 +1,132 @@
+package org.maxkey.web.authorize.endpoint;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLEncoder;
+
+import javax.net.ssl.HttpsURLConnection;
+
+public class CasRestClient {
+        
+    
+    public static void main(String... args) throws Exception
+    {
+        String username ="admin";
+        String password ="maxkey";
+        validateFromCAS(username,password);
+    }
+    
+    public static boolean validateFromCAS(String username, String password) throws Exception
+    {
+        
+        String url = "https://sso.maxkey.top/maxkey/authz/cas/v1/tickets";
+        try 
+        {
+            org.maxkey.client.utils.HttpsTrusts.beforeConnection();
+            HttpsURLConnection hsu = (HttpsURLConnection)openConn(url);
+            String s =   URLEncoder.encode("username","UTF-8") + "=" + URLEncoder.encode(username,"UTF-8");
+            s+="&" +URLEncoder.encode("password","UTF-8") + "=" + URLEncoder.encode(password,"UTF-8");
+            
+            System.out.println(s);
+            OutputStreamWriter out = new OutputStreamWriter(hsu.getOutputStream());
+            BufferedWriter bwr = new BufferedWriter(out); 
+            bwr.write(s);
+            bwr.flush();
+            bwr.close();
+            out.close();
+            
+            String tgt = hsu.getHeaderField("location");
+            System.out.println( hsu.getResponseCode());
+            if(tgt != null && hsu.getResponseCode() == 201)
+            {
+                System.out.println(tgt);
+                
+                System.out.println("Tgt is : " + tgt.substring( tgt.lastIndexOf("/") +1));
+                tgt = tgt.substring( tgt.lastIndexOf("/") +1);
+                bwr.close();
+                closeConn(hsu);
+                
+                
+                String serviceURL = "http://cas.demo.maxkey.top:8080/demo-cas/";
+                String encodedServiceURL = URLEncoder.encode("service","utf-8") +"=" + URLEncoder.encode(serviceURL,"utf-8");
+                System.out.println("Service url is : " + encodedServiceURL);
+                
+                
+                
+                String myURL = url+ "/"+ tgt ;
+                System.out.println(myURL);
+                hsu = (HttpsURLConnection)openConn(myURL);
+                out = new OutputStreamWriter(hsu.getOutputStream());
+                bwr = new BufferedWriter(out); 
+                bwr.write(encodedServiceURL);
+                bwr.flush();
+                bwr.close();
+                out.close();
+                
+                System.out.println("Response code is:  " + hsu.getResponseCode());
+                
+                BufferedReader isr = new BufferedReader(   new InputStreamReader(hsu.getInputStream()));
+                String line;
+                System.out.println( hsu.getResponseCode());
+                while ((line = isr.readLine()) != null) {
+                    System.out.println( line);
+                }
+                isr.close();
+                hsu.disconnect();
+                return true;
+                
+            }
+            else
+            {
+                return false;
+            }
+            
+            
+        }
+        catch(MalformedURLException mue)
+        {
+            mue.printStackTrace();
+            throw mue; 
+             
+        }
+        catch(IOException ioe)
+        {
+            ioe.printStackTrace();
+            throw ioe;
+        }
+        
+        
+        
+        
+        
+    }
+    
+    
+    static URLConnection openConn(String urlk)  throws MalformedURLException, IOException
+    {
+        
+        URL url = new URL(urlk);
+        HttpsURLConnection hsu = (HttpsURLConnection) url.openConnection();
+        hsu.setDoInput(true);
+        hsu.setDoOutput(true);
+        hsu.setRequestMethod("POST");
+        return hsu;
+        
+        
+    }
+    
+    
+    static void closeConn(HttpsURLConnection c)
+    {
+        c.disconnect();
+    }
+    
+    
+    }
+

+ 51 - 0
maxkey-protocols/maxkey-protocol-cas/src/test/java/org/maxkey/web/authorize/endpoint/RestTestClient.java

@@ -0,0 +1,51 @@
+package org.maxkey.web.authorize.endpoint;
+/*
+import org.pac4j.cas.profile.CasRestProfile;
+import org.pac4j.cas.client.rest.CasRestFormClient;
+import org.pac4j.cas.config.CasConfiguration;
+import org.pac4j.cas.credentials.authenticator.CasRestAuthenticator;
+import org.pac4j.cas.profile.CasProfile;
+import org.pac4j.core.context.JEEContext;
+import org.pac4j.core.context.WebContext;
+import org.pac4j.core.credentials.TokenCredentials;
+import org.pac4j.core.credentials.UsernamePasswordCredentials;
+import org.pac4j.core.exception.HttpAction;
+import org.springframework.mock.web.MockHttpServletRequest;
+import org.springframework.mock.web.MockHttpServletResponse;
+
+import java.util.Map;
+import java.util.Set;
+
+public class RestTestClient {
+
+    public static void main(String[] args ) throws HttpAction {
+        final String casUrlPrefix = "http://localhost:8080/cas";
+        String username = args[0];
+        String password = args[1];
+        String serviceUrl = args[2];
+        CasConfiguration casConfiguration = new CasConfiguration(casUrlPrefix);
+        final CasRestAuthenticator authenticator = new CasRestAuthenticator(casConfiguration);
+        final CasRestFormClient client = new CasRestFormClient(casConfiguration,"username","password");
+        final MockHttpServletRequest request = new MockHttpServletRequest();
+        final MockHttpServletResponse response = new MockHttpServletResponse();
+
+        final WebContext webContext = new JEEContext(request, response);
+        casConfiguration.init(webContext);
+        UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(username,password,"testclient");
+        CasRestAuthenticator restAuthenticator = new CasRestAuthenticator(casConfiguration);
+        // authenticate with credentials (validate credentials)
+        restAuthenticator.validate(credentials, webContext);
+        final CasRestProfile profile = (CasRestProfile) credentials.getUserProfile();
+        // get service ticket
+        final TokenCredentials casCredentials = client.requestServiceTicket(serviceUrl, profile, webContext);
+        // validate service ticket
+        final CasProfile casProfile = client.validateServiceTicket(serviceUrl, casCredentials, webContext);
+        Map<String,Object> attributes = casProfile.getAttributes();
+        Set<Map.Entry<String,Object>> mapEntries = attributes.entrySet();
+        for (Map.Entry entry : mapEntries) {
+            System.out.println(entry.getKey() + ":" + entry.getValue());
+        }
+        client.destroyTicketGrantingTicket(profile,webContext);
+    }
+    
+}*/

+ 3 - 0
maxkey-web-maxkey/src/main/java/org/maxkey/MaxKeyMvcConfig.java

@@ -112,6 +112,9 @@ public class MaxKeyMvcConfig implements WebMvcConfigurer {
                 //cas3.0 Validate
                 .excludePathPatterns("/authz/cas/p3/serviceValidate")
                 .excludePathPatterns("/authz/cas/p3/proxyValidate")
+                //rest
+                .excludePathPatterns("/authz/cas/v1/tickets")
+                .excludePathPatterns("/authz/cas/v1/tickets/*")
                 
                 //OAuth
                 .addPathPatterns("/oauth/v20/authorize")