วันจันทร์ที่ 24 พฤษภาคม พ.ศ. 2553

วิธีการ set redirect แบบ permanent บน virtualhost

โจทย์มีอยู่ว่าผมจะ redirect example.com ไปที่ www.example.com โดยต้อง redirect แบบ permanent ในขั้นแรกผมจะต้องมี virtualhost ที่เป็น www ก่อนคือ

  1. www.example.com โดยในไฟล์ใส่ค่าเป็น
    <virtualhost *>
    ServerAdmin     webmaster@example.com 
    DocumentRoot    /var/www/example.com/ 
    ServerName      www.example.com 
    </virtualhost>
    
  2. จากนั้นสร้าง virtualhost อีกไฟล์เป็น example.com
    <virtualhost *>
    ServerName      example.com
    redirect        permanent / http://www.example.com/
    </virtualhost>

เมื่อสร้างไฟล์เสร็จแล้วให้ใช้คำสั่ง a2ensite เพื่อ enable site แต่ละ site และให้ reload apache ด้วย

ผมใช้ path บน web ตามชื่อ domain และไฟล์ virtualhost เป็นชื่อ domain เหมือนกันเพื่อกันความสับสนครับ หากใครอยากใช้ชื่ออื่นก็สามารถทำได้ครับ
Continue Reading...

วันศุกร์ที่ 14 พฤษภาคม พ.ศ. 2553

ติดตั้ง bazaar v2.x ใน ubuntu

เนื่องจาก bzr ใน ubuntu ถ้าติดตั้งด้วยวิธี apt-get install แล้ว default นั้นเป็น v1.3 ซึ่งโดยส่วนมากแล้วจะต้องใช้ v1.6+ ขึ้นไป ดังนั้นจึงต้องทำให้มันเป็น v1.6+ ซะก่อน

ดังนั้นก็เลย search หากับพี่กูซะหน่อยไปเจอ http://wiki.bazaar.canonical.com/DistroDownloads#Ubuntu ซึ่งได้บอกวิธีไว้ว่าใช้ add source list เพิ่มโดยใช้ repository ดังนี้

ขั้นตอนคือไปแก้ที่ file /etc/apt/source.list และเพิ่ม list ด้านล่างลงไป

deb http://ppa.launchpad.net/bzr/ppa/ubuntu YOUR_UBUNTU_VERSION_HERE main 
deb-src http://ppa.launchpad.net/bzr/ppa/ubuntu YOUR_UBUNTU_VERSION_HERE main

โดยสำหรับผม ใช้ 8.04 server ดังนั้นจึงใช้

deb http://ppa.launchpad.net/bzr/ppa/ubuntu hardy main 
deb-src http://ppa.launchpad.net/bzr/ppa/ubuntu hardy main 

จากนั้นให้พิมพ apt-get update แล้วค่อยติดตั้ง bzr ตามปกติ
Continue Reading...

วันพุธที่ 5 พฤษภาคม พ.ศ. 2553

Bonita and Ldap Configuration (Authenticate User)

หลังจากที่เราได้ทำ ldap module login สำหรับ bonita กันไปแล้ว ปัญหาก็ได้เกิดขึ้นอีก เนื่องจากเราต้องการ map ให้แต่ละ activity ถูกเรียกใช้งานด้วย group ที่ต่างกันเช่น

group sales เป็นผู้สร้าง Quotation ขั้นต่อไปคือการ sign Quotation ซึ่งต้องเป็น group manager

โดย bonita นั้นมี connector ที่ช่วยเราทำงานในลักษณะนี้อยู่แล้ว ซึ่งเราสามารถใส่ host, port, protocol, username, password รวมถึงกำหนด base group, base people แม้กระทั่ง time limit ก็สามารถตั้งค่าได้ แต่ดันไม่มีช่องที่ map attribute กับ username ที่ login มาให้ใส่ค่าซะงั้น

ก็ต้องมาแกะ code กันเหมือนเดิม ซึ่ง code ในส่วนนี้จะอยู่ที่ bonita-connector ปรากฏว่ามีจุดที่แปลกๆ อยู่ 2 จุดคือ
  1. ที่ method getGroupLdapConenctor ในclass LdapRoleResolver ดันมีการ set ค่า attribute ดื้อๆ เป็น hardcode ซะงั้น
  2. ที่ method getMembersSet ใน class LdapRoleResolver มีการเรียกใช้ method execute() ของ class LdapConnector ถึง 2 ครั้ง (อาจมีความตั้งใจอะไรบางอย่างแต่ผมไม่เข้าใจ)

** LdapRoleResolver และ LdapConnector อยู่ที่ bonita-connectors-5.1/ldap/src/main/java/org/bonitasoft/connectors/ldap/

 ดังนั้นจึงต้องแก้ code ใน class นี้ซะได้เป็น

method getGroupLdapConenctor (สงสัยรีบจัดเลยเขียนชื่อ method ผิด)

private LdapConnector getGroupLdapConenctor() {
 LdapConnector ldap = new LdapConnector();
 ldap.setHost(host);
 ldap.setPort(port);
 ldap.setProtocol(protocol);
 ldap.setUserName(userName);
 ldap.setPassword(password);
 //ldap.setAttributes("uniqueMember");  แก้จาก uniqueMember เป็น uid
 ldap.setAttributes("uid");
 ldap.setBaseObject(baseObjectGroup);
 ldap.setDerefAliases(derefAliases);
 ldap.setFilter(filter);
 ldap.setReferralHandling(referralHandling);
 ldap.setScope(scope);
 ldap.setSizeLimit(sizeLimit);
 ldap.setTimeLimit(timeLimit);
 return ldap;
}

และ method getMembersSet

@Override
protected Set<String> getMembersSet(String roleId) throws Exception {
 LdapConnector ldap = getGroupLdapConenctor();
     ldap.execute();

     Set<String> returns = new HashSet<String>();
     List<List<LdapAttribute>> list = ldap.getLdapAttributeList();
     if (!list.isEmpty()) {
                // comment ส่วนที่ เรียกซ้ำซ้อน (จริงๆ ไม่รู้ว่าเขาใช้ทำอะไร)
        /*List<LdapAttribute> members = list.get(0);
        StringBuilder builder = new StringBuilder();
        if (members.size() > 1) {
          builder.append("(|");
        }
        for (LdapAttribute member : members) {
          builder.append("(");
          String value = member.getValue();
          int index = value.indexOf(",");
   builder.append(value.substring(0, index));
   builder.append(")");
        }
        if (members.size() > 1) {
          builder.append(")");
        }
        ldap.setBaseObject(baseObjectPeople);
        ldap.setFilter(builder.toString());
        ldap.setAttributes("uid");
        ldap.execute();
        list = ldap.getLdapAttributeList();*/
        for (List<LdapAttribute> uids : list) {
          returns.add(uids.get(0).getValue());
        }
     }
 return returns;
}


จากนั้นต้องไปแก้ไข method executeConnector ที่ไฟล์ LdapConnector ด้วยตามนี้

if (getProtocol() == LdapProtocol.TLS) {

comment ส่วนตรวจสอบ response

      //StartTlsRequest request = new StartTlsRequest();

      //response = (StartTlsResponse) ctx.extendedOperation(request);

      //response.negotiate();

      if (getUserName() != null && getPassword() != null) {

        ctx.addToEnvironment(Context.SECURITY_AUTHENTICATION, "simple");

        ctx.addToEnvironment(Context.SECURITY_PRINCIPAL, getUserName());

        ctx.addToEnvironment(Context.SECURITY_CREDENTIALS, getPassword());

      }

    }

และ

    if (getProtocol() == LdapProtocol.TLS) {

      //response.close();

    }


เนื่องจากหากไม่ comment ส่วนดังกล่าวจะทำให้เกิด error ขึ้น ไม่แน่ใจว่าเกิดจาก zimbra ไม่ยอมตอบเมื่อมีการ request ไปหรือเปล่า

จากนั้นให้ build code ในส่วนนี้ ซึ่ง build แล้วจะได้ไฟล์ ldap-5.x.jar จากนั้นให้นำไฟล์ดังกล่าวไปยัดไว้ที่ studio ที่ path

BOS-5.x/studio/workspace/My Extensions/provided-libs/ldap/

แล้วค่อย export bar ไฟล์ใหม่อีกครั้ง ซึ่ง bar นี้จะสามารถ group user ได้ตามที่ตั้งค่าไว้ใน process

-----------------------------------------

thanks for @iporsut
Continue Reading...

Bonita and Ldap Configuration (Module Login)

เนื่องจาก bonita นั้นไม่มี module login ที่ใช้ ldap ในการ authentication ดังนั้นเราต้อง implement ส่วนนี้ขึ้นมาเอง โดยขั้นตอนที่เราต้องทำคือสร้าง class ที่ implement interface AuthenticationService

public interface AuthenticationService {
    /**
     * Check whether a user has admin privileges or not
     * @param username the user's username
     * @return true if the user has admin privileges, false
  otherwise
     * @throws UserNotFoundException
     */
    boolean isUserAdmin(String username) throws UserNotFoundException;
    /**
     * Check some user's credentials
     * @param username the user's username
     * @param password the user's password
     * @return true if the credentials are valid, false otherwise
     */
    boolean checkUserCredentials(String username, String password);
}  

ตัวอย่าง class ที่ implement เรียบร้อยแล้ว

public class LdapZimbraLoginModule implements AuthenticationService {

    private String connection;
    private String ldapDN;

    public LdapZimbraLoginModule(String connection, String ldapDN) {
        super();
        this.connection = connection;
        this.ldapDN = ldapDN;
    }

    public boolean isUserAdmin(String username) throws UserNotFoundException {
        Hashtable env = new Hashtable();
        env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
        env.put(Context.PROVIDER_URL, "ldap://domain:port");
        env.put(Context.SECURITY_PRINCIPAL, username);
        env.put(Context.SECURITY_CREDENTIALS, password);
        try {
            DirContext ctx = new InitialDirContext(env);
            SearchControls sc = new SearchControls();
            sc.setSearchScope(SearchControls.ONELEVEL_SCOPE);
            NamingEnumeration results = ctx.search("ou=people,dc=example,dc=com", "(zimbraIsAdminAccount=TRUE)", sc);
            while (results.hasMore()) {
                SearchResult searchResult = (SearchResult) results.next();
                Attributes attributes = searchResult.getAttributes();
                Attribute attr = attributes.get("uid");
                if (username.trim().equals(attr.get().toString().trim())){
                    return true;
                }
            }
            ctx.close();
        } catch (javax.naming.NamingException r) {
        }
        return false;
    }

    public boolean checkUserCredentials(String username, String password) {
            Hashtable env = new Hashtable();
            env.put(Context.INITIAL_CONTEXT_FACTORY, 
      "com.sun.jndi.ldap.LdapCtxFactory");
            env.put(Context.PROVIDER_URL, this.connection);
            env.put(Context.SECURITY_PRINCIPAL, 
      "uid=" + 
      username.split("@")[0] + 
      ",ou=people," + 
      this.ldapDN);
            env.put(Context.SECURITY_CREDENTIALS, password);
            DirContext ctx;
            try {
                ctx = new InitialDirContext(env);
                ctx.close();
                return true;
            } catch (javax.naming.NamingException r) {
            }
            return false;
    }
}

และควรจะนำไปไว้ที่ bonita-runtime-5.1/bonita-server/src/main/java/org/ow2/bonita/services/impl/ เพื่อ ความเป็นระเบียบ จากนั้นให้ build code ในส่วนของ bonita-server จะได้ไฟล์ bonita-server-5.x.jar

ให้นำ jar ไฟล์ที่ได้ไป deploy ที่ server ของเรา (ทับของเก่าเลยนะครับ)

TOMCAT_ROOT/lib/
** TOMCAT_ROOT คือ tomcat ที่ได้มาจากการ build ตัว console ของ bonita

จากนั้นไปแก้ไข authentication service ให้ไปเรียก class ที่เราสร้างขึ้นที่ไฟล์

TOMCAT_ROOT/conf/bonita-environment.xml

โดยแก้ไขจาก

<authentication-service class="org.ow2.bonita.services.impl.DbAuthentication" name="authentication-service">
  <arg>

    <string value="bonita-session:core"></string>
  </arg>
</authentication-service>


ให้เป็น

<authentication-service name='authentication-service' class='org.ow2.bonita.services.impl.LdapZimbraLoginModule'>
           <arg><string value='ldap://domain:port' /></arg>
           <arg><string value='dc=example,dc=com' /></arg>
</authentication-service>


โดยค่า <arg> คือ parameter ที่ส่งไปยัง class LdapZimbraLoginModule

สรุปหลักการโดยคร่าวๆ คือให้สร้าง class ที่ implement AuthenticationService interface เพื่อเป็นตัว check ว่าจะ login ผ่านเมื่อใด จากนั้นแก้ไขให้ server ไปเรียกใช้ class นั้นครับ

วิธีข้างต้นเป็นวิธีที่ ดิบ+เถื่อนนะครับ เกิดจากการแกะ code แล้วแก้เอาดื้อๆ ซึ่งจริงๆแล้ว มีวิธีที่ดีกว่านี้อยู่ครับ ตามนี้เลย http://www.bonitasoft.org/forum/viewtopic.php?id=2397
ซึ่งผมอ่านแล้วไม่ค่อยเข้าใจเท่าไร

-------------------------------------------------------

thanks for @iporsut
Continue Reading...

Blogroll

About