LdapAccountValidator.java

package io.featureprobe.api.auth;

import io.featureprobe.api.base.enums.MemberSourceEnum;
import io.featureprobe.api.base.enums.MemberStatusEnum;
import io.featureprobe.api.base.enums.OperationType;
import io.featureprobe.api.base.enums.OrganizationRoleEnum;
import io.featureprobe.api.base.model.OrganizationMemberModel;
import io.featureprobe.api.base.tenant.TenantContext;
import io.featureprobe.api.dao.entity.Member;
import io.featureprobe.api.dao.entity.OperationLog;
import io.featureprobe.api.dao.entity.Organization;
import io.featureprobe.api.dao.entity.OrganizationMember;
import io.featureprobe.api.dao.repository.OrganizationRepository;
import io.featureprobe.api.dto.MemberCreateRequest;
import io.featureprobe.api.service.MemberService;
import io.featureprobe.api.service.OperationLogService;
import io.featureprobe.api.service.OrganizationService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Primary;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.ldap.core.support.LdapContextSource;
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Component;

import java.util.*;

import static org.springframework.ldap.query.LdapQueryBuilder.query;


@Component("ldap")
@AllArgsConstructor
@Slf4j
public class LdapAccountValidator implements AccountValidator{

    private MemberService memberService;

    private OperationLogService operationLogService;


    private OrganizationRepository organizationRepository;


    private LdapTemplate ldapTemplate;

    private LdapContextSource contextSource;

    private String ldapUsernameAttribute;

    @Override
    public Authentication authenticate(Authentication authentication) {
        UserPasswordAuthenticationToken token = (UserPasswordAuthenticationToken) authentication;
        if (StringUtils.isNotBlank(token.getAccount()) && StringUtils.isNotBlank(token.getPassword())) {
            boolean ldapAuthPassCheck = authenticateByLdap(token);
            String memberName = token.getAccount();
            Optional<Member> memberOptional =  memberService.findByAccount(memberName);
            if (!memberOptional.isPresent() && ldapAuthPassCheck) {
                OrganizationMemberModel defaultOrganizeMemberModel =
                        new OrganizationMemberModel(1L, "Default Organize", OrganizationRoleEnum.WRITER);
                Organization organization = organizationRepository.findById(1L).get();
                Member member = new Member();
                member.setAccount(token.getAccount());
                member.setPassword(new BCryptPasswordEncoder().encode(UUID.randomUUID().toString()));
                member.setSource(MemberSourceEnum.PLATFORM.name());
                member.addOrganization(organization,OrganizationRoleEnum.WRITER);
                memberService.save(member);
                return new UserPasswordAuthenticationToken(AuthenticatedMember.create(member,
                        defaultOrganizeMemberModel),
                        Collections.emptyList());
            }
            OperationLog log = new OperationLog(OperationType.LOGIN.name() + "_" + token.getSource(),
                        token.getAccount());
            Member member = memberOptional.get();
            Member immutableMember = new Member();
            BeanUtils.copyProperties(member, immutableMember);
            if (!memberOptional.isPresent() || isAccessTokenNumber(memberOptional)) {
                throw new UsernameNotFoundException("Account not found.");
            }
            boolean passwordMatched = new BCryptPasswordEncoder().matches(token.getPassword(),
                    member.getPassword());
            OrganizationMember organizationMember = member.getOrganizationMembers().get(0);
            OrganizationMemberModel organizationMemberModel = new OrganizationMemberModel(
                    organizationMember.getOrganization().getId(),
                    organizationMember.getOrganization().getName(), organizationMember.getRole());
            if (ldapAuthPassCheck || passwordMatched) {
                if (MemberStatusEnum.ACTIVE.name().equals(member.getStatus().name())) {
                    member.setVisitedTime(new Date());
                    memberService.save(member);
                    operationLogService.save(log);
                    return new UserPasswordAuthenticationToken(AuthenticatedMember.create(immutableMember,
                            organizationMemberModel), Collections.emptyList());
                }
            }
            throw new BadCredentialsException("Credentials are incorrect.");
        }
        throw new BadCredentialsException("Credentials are incorrect.");
    }

    private boolean authenticateByLdap(UserPasswordAuthenticationToken token) {
        try {
            ldapTemplate.authenticate(query().where(ldapUsernameAttribute).is(token.getAccount()),token.getPassword());
            return true;
        } catch (Exception e) {
            log.error("err when authenticate on ldap server {}",e.getMessage());
        }
        return false;
    }

    private boolean isAccessTokenNumber(Optional<Member> member) {
        return StringUtils.equalsIgnoreCase(member.get().getSource(), MemberSourceEnum.ACCESS_TOKEN.name());
    }
}